[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n/app/build\n.idea\n"
  },
  {
    "path": "README.md",
    "content": "# CardStackViewpager 卡片翻页效果的Viewpager\n\n这个`CardStackViewpager `的灵感来自Github上面的\t[`FlippableStackView`](https://github.com/blipinsk/FlippableStackView)开源项目，而我想实现的效果方向上恰好与`FlippableStackView`相反，并且细节上也有些区别，详见下面的效果对比图：\n\n###### FlippableStackView运行效果图：\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/two.gif?raw=true)\n\n###### CardStackViewpager运行效果图：\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/one.gif?raw=true)\n\n这里讲一个小插曲，自己尝试实现`CardStackViewpager`的过程中，由于一开始对`PageTransformer`的`onTransform(View page, float position)`实在很困惑，于是我用自己小学般的英语写了一封邮件给`FlippableStackView`的开发者，尴尬的是，至今他没回我邮件。\n\n回归正题，下面我就来具体讲一下`CardStackViewpager `的实现思路，其实整个核心就在下面这一段代码，把下面这段代码搞懂了，就可以通过自定义自己的`PageTransformer`实现各种各样想要的Viewpager效果了。\n\n#### 核心的VerticalStackTransformer的onTransform方法最终版\n\n```\n    @Override\n    protected void onTransform(View page, float position) {\n        if (position <= 0.0f) {\n            page.setAlpha(1.0f);\n            Log.e(\"onTransform\", \"position <= 0.0f ==>\" + position);\n            page.setTranslationY(0f);\n            //控制停止滑动切换的时候，只有最上面的一张卡片可以点击\n            page.setClickable(true);\n        } else if (position <= 3.0f) {\n            Log.e(\"onTransform\", \"position <= 3.0f ==>\" + position);\n            float scale = (float) (page.getWidth() - ScreenUtils.dp2px(context, spaceBetweenFirAndSecWith * position)) / (float) (page.getWidth());\n            //控制下面卡片的可见度\n            page.setAlpha(1.0f);\n            //控制停止滑动切换的时候，只有最上面的一张卡片可以点击\n            page.setClickable(false);\n            page.setPivotX(page.getWidth() / 2f);\n            page.setPivotY(page.getHeight() / 2f);\n            page.setScaleX(scale);\n            page.setScaleY(scale);\n            page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - scale) + ScreenUtils.dp2px(context, spaceBetweenFirAndSecHeight) * position);\n        }\n    }\n```\n\n###### 在分析上面的代码之前，我们需要有以下几个知识准备：\n1. Viewpager的`setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)`方法的第一个参数，用来控制加入到Viewpager的Views对象是正序的还是倒序的，这里为了实现我们想要的效果，需要让第一个添加到布局的View来到第一个展示，所以传入`true`；\n2. Viewpager的`setOffscreenPageLimit(int limit)`方法，设置有多少的缓存Views，这个将决定我们的卡片重叠展示的效果显示几层卡片效果。\n\n现在我们继续看上面的`onTransform(View page, float position)`方法，这个方法设计的很巧妙，当初我在探索的时候，通过打印日志来判断这个方法是如何执行的时候，发现这这个`position`的值看似毫无规律，后来我想到以前数学里推理定理时的方法，从`特殊情况入手`,再`一点点分析其他情况`，然后一步步的实现上面的代码。\n\n#### 第一步，分析应用初始化进来的时候的position\n此时的`onTransform(View page, float position)`方法如下：\n\n```\n    @Override\n    protected void onTransform(View page, float position) {\n        Log.e(\"onTransform\",\"position  ==>\"+position);\n        //设置每个卡片y方向偏移量，这样可以使卡片都完全叠加起来\n        page.setTranslationY(-page.getHeight() * position);\n    }\n```\n\n对应日志如下：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/three.png?raw=true)\n\n根据这个日志很明显的可以判断得到：由于我现在设置的`setOffscreenPageLimit(int limit)`值为4，所以可以看到position有上面几种情况，显而易见，每个position对应了一张卡片，这个时候界面的效果如图：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/five.png?raw=true)\n\n现在猜想2,3,4,5号卡片就在1号卡片下面，现在要想个法子证实我们的猜想，将`onTransform(View page, float position)`方法改成下面这样：\n\n```\n    @Override\n    protected void onTransform(View page, float position) {\n        Log.e(\"onTransform\",\"position  ==>\"+position);\n        //设置卡片透明度\n        page.setAlpha(0.5f);\n        //设置缩放中点\n        page.setPivotX(page.getWidth() / 2f);\n        page.setPivotY(page.getHeight() / 2f);\n        //设置缩放的比例 此处设置两个相邻的卡片的缩放比率为0.9f\n        page.setScaleX((float) Math.pow(0.9f,position));\n        page.setScaleY((float) Math.pow(0.9f,position));\n\t\t//设置每个卡片y方向偏移量，这样可以使卡片都完全叠加起来\n\t\tpage.setTranslationY(-page.getHeight() * position);\n    }\n```\n\n运行起来之后，证实了我们的想法：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/six.png?raw=true)\n\n#### 第二步，实现卡片叠加的最终效果\n\n分析上面的图片效果，可以发现，把第二张卡片往下移动一段距离之后，就可以形成一个卡片叠加的初步效果了，变成下面这样：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/seven.png?raw=true)\n\n其他的卡片，道理一样，那么如何实现这个向下偏移的值呢，这个值如何以一个表达式表现出来呢，先看下面的A,B,C步骤的分析图：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/eight.png?raw=true)\n\n显而易见，相隔两张卡片的偏移量为：`(H2-H1)+d1`,我们稍微改变一下`onTransform(View page, float position)`方法如下：\n\n```\n@Override\n    protected void onTransform(View page, float position) {\n        Log.e(\"onTransform\", \"position  ==>\" + position);\n        page.setAlpha(0.5f);\n        page.setPivotX(page.getWidth() / 2f);\n        page.setPivotY(page.getHeight() / 2f);\n        page.setScaleX((float) Math.pow(0.9f, position));\n        page.setScaleY((float) Math.pow(0.9f, position));\n        //修改过的代码\n        page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - (float) Math.pow(0.9f, position)) + ScreenUtils.dp2px(context, 10));\n    }\n```\n\n此时的效果图如下：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/nine.png?raw=true)\n\n卡片半透明的时候，效果还不是特别的明显，把`page.setAlpha(0.5f)`改为`page.setAlpha(1.0f)`再试一次：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/ten.png?raw=true)\n\n惊喜的发现这不就是卡片叠加效果嘛，虽然现在的效果细节还有点问题，我们不急，这个细节问题简单分析一下就会想到，是我们的缩放比例问题导致的，继续下一步的优化，我们将会解决这个问题。\n\n#### 第三步，根据相邻卡片的间距值动态设置缩放值\n\n上面的`onTransform(View page, float position)`方法中，我们的x，y缩放比例都是写的一个固定值`0.9f`,这个显然不能满足日常需求，我这里是设置上下两张卡片的宽度比来作为最终想要的缩放比例，修改`onTransform(View page, float position)`方法如下：\n\n```\n    @Override\n    protected void onTransform(View page, float position) {\n        Log.e(\"onTransform\", \"position  ==>\" + position);\n        float scale = (float) (page.getWidth() - ScreenUtils.dp2px(context, 20 * position)) / (float) (page.getWidth());\n        page.setAlpha(1.0f);\n        page.setPivotX(page.getWidth() / 2f);\n        page.setPivotY(page.getHeight() / 2f);\n        page.setScaleX(scale);\n        page.setScaleY(scale);\n        //修改过的代码\n        page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - scale) + ScreenUtils.dp2px(context, 10) * position);\n    }\n```\n\n再跑一下程序，完美的卡片效果就出现了：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/eleven.png?raw=true)\n\n#### 第四步，特殊到一般，实现最终的卡片滑动效果\n\n此时，我们尝试一下滑动Viewpager，发现卡片的切换效果并没有如期的出现，通过多次尝试和分析，我发现，由于我们这里没有对当前滑动过去的那张卡片做特殊处理，这里的特殊处理指的是：为了实现卡片抽动的切换效果，当前滑动的卡片应该不用执行任何缩放和偏移的操作，修改为`page.setTranslationY(0f);`,具体代码如下：\n\n>这里列出一篇博客：\t[http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0814/1650.html](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0814/1650.html)，他主要讲了对`onTransform(View page, float position)`中position的理解\n\n```\n    @Override\n    protected void onTransform(View page, float position) {\n        Log.e(\"onTransform\", \"position  ==>\" + position);\n        if (position <= 0.0f) {\n            page.setAlpha(1.0f);\n            //出现卡片抽动效果的关键代码\n            page.setTranslationY(0f);\n        } else {\n            float scale = (float) (page.getWidth() - ScreenUtils.dp2px(context, 20 * position)) / (float) (page.getWidth());\n            page.setAlpha(1.0f);\n            page.setPivotX(page.getWidth() / 2f);\n            page.setPivotY(page.getHeight() / 2f);\n            page.setScaleX(scale);\n            page.setScaleY(scale);\n            //修改过的代码\n            page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - scale) + ScreenUtils.dp2px(context, 10) * position);\n        }\n    }\n```\n\n至此，已经可以实现文章开头的动画效果了。回头想一下，我们一直在基于特殊的情况写代码，最后发现其实他就是所有一般情况中的一种，只不过特殊情况由于他的特殊性最容易进行分析总结，更有利于我们编写出易懂的代码。\n\n最后补充下，在实际项目中，在每张卡片上可能还有有点击区域，更可能整张卡片都是一个点击区域，这个时候就会发现一个问题，当处于这种情况的时候：\n\n![enter image description here](https://github.com/NateRobinson/CardStackViewpager/blob/master/img/eleven.png?raw=true)\n\n我不但可以点到卡片1，也会点到卡片2，卡片3。。。这样肯定不行的，所以我们再次回到`onTransform(View page, float position)`方法，在里面加一个控制：\n\n```\n   @Override\n    protected void onTransform(View page, float position) {\n        Log.e(\"onTransform\", \"position  ==>\" + position);\n        if (position <= 0.0f) {\n            //最上面的卡片可以点击\n            page.setClickable(true);\n            .......\n        } else {\n            //下面的卡片不可点击\n            page.setClickable(false);\n            ........\n        }\n    }\n```\n\n另外我们可能只需要4张卡片重叠的效果就行，这个时候改变一下判断条件即可：\n\n```\n   @Override\n    protected void onTransform(View page, float position) {\n        Log.e(\"onTransform\", \"position  ==>\" + position);\n        if (position <= 0.0f) {\n            ......\n        //控制显示几张卡片\n        } else if(position <= 3.0f) {\n\t\t    ......\n        }\n    }\n```\n\n至此这边文章就要结束了，这是我的总结，希望能帮助大家对`onTransform(View page, float position)`方法有一个更深的理解。\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 27\n    buildToolsVersion \"28.0.2\"\n\n    defaultConfig {\n        applicationId \"com.gu.cardstackviewpager\"\n        minSdkVersion 15\n        targetSdkVersion 27\n        versionCode 1\n        versionName \"1.0.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    testImplementation 'junit:junit:4.12'\n    implementation 'com.android.support:appcompat-v7:27.1.1'\n    implementation project(':library')\n    implementation 'com.android.support:cardview-v7:27.1.1'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in D:\\AndroidTools\\sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/androidTest/java/com/gu/cardstackviewpager/ApplicationTest.java",
    "content": "package com.gu.cardstackviewpager;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.gu.cardstackviewpager\">\n\n  <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n  <application\n      android:allowBackup=\"true\"\n      android:icon=\"@mipmap/app_logo\"\n      android:label=\"@string/app_name\"\n      android:theme=\"@style/AppTheme\">\n    <!-- 首页-->\n    <activity\n        android:name=\".activity.HomeActivity\"\n        android:configChanges=\"orientation|keyboardHidden|screenSize\"\n        android:label=\"@string/app_name\"\n        android:screenOrientation=\"portrait\"\n        android:windowSoftInputMode=\"stateAlwaysHidden\">\n      <intent-filter>\n        <action android:name=\"android.intent.action.MAIN\"/>\n        <category android:name=\"android.intent.category.LAUNCHER\"/>\n      </intent-filter>\n    </activity>\n\n    <!-- 关于我的界面-->\n    <activity\n        android:name=\".activity.AboutActivity\"\n        android:configChanges=\"orientation|keyboardHidden|screenSize\"\n        android:screenOrientation=\"portrait\"\n        android:windowSoftInputMode=\"stateAlwaysHidden\"/>\n\n    <!-- webview界面-->\n    <activity\n        android:name=\".activity.WebViewActivity\"\n        android:configChanges=\"orientation|keyboardHidden|screenSize\"\n        android:screenOrientation=\"portrait\"\n        android:windowSoftInputMode=\"stateAlwaysHidden\"/>\n\n    <!--<meta-data-->\n        <!--android:name=\"android.max_aspect\"-->\n        <!--android:value=\"2.4\" />-->\n\n  </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/gu/cardstackviewpager/activity/AboutActivity.java",
    "content": "package com.gu.cardstackviewpager.activity;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\n\nimport com.gu.cardstackviewpager.R;\n\n/**\n * Created by Nate on 2016/7/22.\n */\npublic class AboutActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        //设置切换动画\n        overridePendingTransition(R.anim.slide_right_in, R.anim.slide_left_out);\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_about);\n\n\n        findViewById(R.id.back_ll).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                finish();\n            }\n        });\n        findViewById(R.id.my_blog_ll).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Bundle bundle = new Bundle();\n                bundle.putString(WebViewActivity.URL_KEY, getString(R.string.my_blog));\n                Intent intent = new Intent(AboutActivity.this, WebViewActivity.class);\n                intent.putExtras(bundle);\n                startActivity(intent);\n            }\n        });\n        findViewById(R.id.my_git_ll).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Bundle bundle = new Bundle();\n                bundle.putString(WebViewActivity.URL_KEY, getString(R.string.my_github));\n                Intent intent = new Intent(AboutActivity.this, WebViewActivity.class);\n                intent.putExtras(bundle);\n                startActivity(intent);\n            }\n        });\n\n    }\n\n\n    @Override\n    public void finish() {\n        super.finish();\n        //设置切换动画\n        overridePendingTransition(R.anim.slide_left_in, R.anim.slide_right_out);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/gu/cardstackviewpager/activity/HomeActivity.java",
    "content": "package com.gu.cardstackviewpager.activity;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\n\nimport com.gu.cardstackviewpager.R;\nimport com.gu.cardstackviewpager.adapter.ContentFragmentAdapter;\nimport com.gu.cardstackviewpager.fragment.CardFragment;\nimport com.gu.library.OrientedViewPager;\nimport com.gu.library.transformer.VerticalStackTransformer;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by Nate on 2016/7/22.\n */\npublic class HomeActivity extends AppCompatActivity {\n\n    private OrientedViewPager mOrientedViewPager;\n    private ContentFragmentAdapter mContentFragmentAdapter;\n    private List<Fragment> mFragments = new ArrayList<>();\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_home);\n\n        mOrientedViewPager = (OrientedViewPager) findViewById(R.id.view_pager);\n\n        //制造数据\n        for (int i = 0; i < 10; i++) {\n            mFragments.add(CardFragment.newInstance(i + 1));\n        }\n\n        mContentFragmentAdapter = new\n                ContentFragmentAdapter(getSupportFragmentManager(), mFragments);\n        //设置viewpager的方向为竖直\n        mOrientedViewPager.setOrientation(OrientedViewPager.Orientation.VERTICAL);\n        //设置limit\n        mOrientedViewPager.setOffscreenPageLimit(4);\n        //设置transformer\n        mOrientedViewPager.setPageTransformer(true, new VerticalStackTransformer(getApplicationContext()));\n        mOrientedViewPager.setAdapter(mContentFragmentAdapter);\n\n        //跳转关于我的界面\n        findViewById(R.id.about_iv).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Intent intent = new Intent(HomeActivity.this, AboutActivity.class);\n                startActivity(intent);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/gu/cardstackviewpager/activity/WebViewActivity.java",
    "content": "package com.gu.cardstackviewpager.activity;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\nimport android.webkit.WebSettings;\nimport android.webkit.WebView;\nimport android.widget.ProgressBar;\n\nimport com.gu.cardstackviewpager.R;\n\n/**\n * Created by Nate on 2016/7/22.\n */\npublic class WebViewActivity extends AppCompatActivity {\n\n    public static final String URL_KEY = \"url_key\";\n\n    private WebView mWebView;\n    private ProgressBar mProgressBar;\n    private String url = \"https://github.com/NateRobinson\";\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        //设置切换动画\n        overridePendingTransition(R.anim.slide_right_in, R.anim.slide_left_out);\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_webview);\n\n        if (getIntent().getExtras() != null) {\n            url = getIntent().getExtras().getString(WebViewActivity.URL_KEY);\n        }\n\n        mWebView= (WebView) findViewById(R.id.web_view);\n        mProgressBar= (ProgressBar) findViewById(R.id.web_view_progress_bar);\n\n        WebSettings mWebSettings = mWebView.getSettings();\n        mWebSettings.setJavaScriptEnabled(true);\n        mWebSettings.setUseWideViewPort(true);\n        mWebSettings.setSupportZoom(true);\n        mWebSettings.setBuiltInZoomControls(true);\n        mWebSettings.setDisplayZoomControls(false);\n        mWebSettings.setUseWideViewPort(true);\n        mWebSettings.setLoadWithOverviewMode(true);\n        mWebView.setWebChromeClient(new WebChromeClient());\n        mWebView.setWebViewClient(new WebViewClient());\n\n        mWebView.loadUrl(url);\n    }\n\n    public class WebChromeClient extends android.webkit.WebChromeClient {\n        @Override\n        public void onProgressChanged(WebView view, int newProgress) {\n            if (newProgress == 100) {\n                mProgressBar.setVisibility(View.GONE);\n            } else {\n                if (mProgressBar.getVisibility() == View.GONE) {\n                    mProgressBar.setVisibility(View.VISIBLE);\n                }\n                mProgressBar.setProgress(newProgress);\n            }\n            super.onProgressChanged(view, newProgress);\n        }\n    }\n\n    class WebViewClient extends android.webkit.WebViewClient {\n\n        @Override\n        public boolean shouldOverrideUrlLoading(WebView view, String url) {\n            view.loadUrl(url);\n            return true;\n        }\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (mWebView.canGoBack()) {\n            mWebView.goBack();\n        } else {\n            super.onBackPressed();\n        }\n    }\n\n\n    @Override\n    public void finish() {\n        super.finish();\n        //设置切换动画\n        overridePendingTransition(R.anim.slide_left_in, R.anim.slide_right_out);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/gu/cardstackviewpager/adapter/ContentFragmentAdapter.java",
    "content": "package com.gu.cardstackviewpager.adapter;\n\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentStatePagerAdapter;\nimport android.support.v4.view.PagerAdapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ContentFragmentAdapter extends FragmentStatePagerAdapter {\n    private List<Fragment> fragments = new ArrayList<>();\n    private int itemPosition= PagerAdapter.POSITION_UNCHANGED;\n\n    public ContentFragmentAdapter(FragmentManager fm, List<Fragment> fragments) {\n        super(fm);\n        this.fragments = fragments;\n    }\n\n    @Override\n    public Fragment getItem(int position) {\n        return fragments.get(position);\n    }\n\n    @Override\n    public int getCount() {\n        return fragments.size();\n    }\n\n    @Override\n    public CharSequence getPageTitle(int position) {\n        return \"\";\n    }\n\n    @Override\n    public int getItemPosition(Object object) {\n        return  getItemPosition();\n    }\n\n    public int getItemPosition() {\n        return itemPosition;\n    }\n    public void setItemPosition(int itemPosition) {\n        this.itemPosition = itemPosition;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/gu/cardstackviewpager/fragment/CardFragment.java",
    "content": "package com.gu.cardstackviewpager.fragment;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.gu.cardstackviewpager.R;\n\n\n/**\n * Created by Nate on 2016/7/22.\n */\npublic class CardFragment extends Fragment {\n    private static final String INDEX_KEY = \"index_key\";\n\n    public static CardFragment newInstance(int index) {\n        CardFragment fragment = new CardFragment();\n        Bundle bdl = new Bundle();\n        bdl.putInt(INDEX_KEY, index);\n        fragment.setArguments(bdl);\n        return fragment;\n    }\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        final View v = inflater.inflate(R.layout.fragment_card, container, false);\n        TextView cardNumTv = (TextView) v.findViewById(R.id.card_num_tv);\n        final Bundle bundle = getArguments();\n        if (bundle != null) {\n            cardNumTv.setText(bundle.getInt(INDEX_KEY, 0) + \"\");\n        }\n        cardNumTv.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Toast.makeText(getActivity(), \"点击了\" + bundle.getInt(INDEX_KEY, 0) + \"\", Toast.LENGTH_SHORT).show();\n            }\n        });\n        return v;\n    }\n}"
  },
  {
    "path": "app/src/main/res/anim/slide_left_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate android:duration=\"300\" android:fromXDelta=\"-100.0%p\" android:toXDelta=\"0.0\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/slide_left_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate android:duration=\"300\" android:fromXDelta=\"0.0\" android:toXDelta=\"-100.0%p\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/slide_right_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate android:duration=\"300\" android:fromXDelta=\"100.0%p\" android:toXDelta=\"0.0\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/anim/slide_right_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate android:duration=\"300\" android:fromXDelta=\"0.0\" android:toXDelta=\"100.0%p\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/drawable/back_iv_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@mipmap/back_icon_pressed\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@mipmap/back_icon_normal\"/>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/custom_ll_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\">\n        <shape>\n            <solid android:color=\"@color/gray_ef\"/>\n        </shape>\n    </item>\n    <item >\n        <shape>\n            <solid android:color=\"@color/white\"/>\n        </shape>\n    </item>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/github_click_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\">\n        <shape>\n            <solid android:color=\"@color/green_dark\"/>\n        </shape>\n    </item>\n    <item >\n        <shape>\n            <solid android:color=\"@color/green\"/>\n        </shape>\n    </item>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/progress_bar_h5.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <item\n        android:id=\"@android:id/background\"\n        android:drawable=\"@android:color/white\"/>\n    <item android:id=\"@android:id/secondaryProgress\">\n        <scale\n            android:drawable=\"@color/progress_bar_color\"\n            android:scaleWidth=\"100%\" />\n    </item>\n    <item android:id=\"@android:id/progress\">\n        <scale\n            android:drawable=\"@color/progress_bar_color\"\n            android:scaleWidth=\"100%\" />\n    </item>\n\n</layer-list>"
  },
  {
    "path": "app/src/main/res/layout/activity_about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"match_parent\"\n              android:background=\"@color/white\"\n              android:orientation=\"vertical\">\n\n\n    <LinearLayout\n        android:id=\"@+id/back_ll\"\n        android:layout_width=\"?attr/actionBarSize\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:gravity=\"center\">\n\n        <ImageView\n            android:layout_width=\"40dp\"\n            android:layout_height=\"40dp\"\n            android:src=\"@drawable/back_iv_bg\"/>\n\n    </LinearLayout>\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"none\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <ImageView\n                android:layout_width=\"120dp\"\n                android:layout_height=\"120dp\"\n                android:layout_gravity=\"center_horizontal\"\n                android:src=\"@mipmap/my_head_icon\"/>\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"@dimen/top_margin_big\"\n                android:drawablePadding=\"@dimen/normal_padding\"\n                android:drawableRight=\"@mipmap/boy_icon\"\n                android:fontFamily=\"sans-serif-condensed\"\n                android:text=\"@string/my_name\"\n                android:textColor=\"@color/text_color\"\n                android:textSize=\"@dimen/text_font_size_big_22sp\"\n                android:textStyle=\"bold\"/>\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginBottom=\"@dimen/bottom_margin_big\"\n                android:layout_marginLeft=\"@dimen/left_margin\"\n                android:layout_marginRight=\"@dimen/right_margin\"\n                android:layout_marginTop=\"@dimen/top_margin_big\"\n                android:gravity=\"center\"\n                android:text=\"@string/my_signature\"\n                android:textColor=\"@color/text_color\"\n                android:textIsSelectable=\"true\"\n                android:textSize=\"@dimen/text_font_size_content_14sp\"/>\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"1px\"\n                android:layout_marginTop=\"@dimen/top_margin\"\n                android:background=\"@color/gray_ef\"/>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"48dp\"\n                android:background=\"@drawable/custom_ll_bg\"\n                android:gravity=\"center_vertical\"\n                >\n\n                <ImageView\n                    android:layout_width=\"28dp\"\n                    android:layout_height=\"28dp\"\n                    android:layout_marginLeft=\"@dimen/left_margin\"\n                    android:src=\"@mipmap/qq_icon\"/>\n\n                <TextView\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_marginLeft=\"@dimen/left_margin_normal\"\n                    android:layout_weight=\"1\"\n                    android:gravity=\"center_vertical\"\n                    android:text=\"@string/my_qq\"\n                    android:textColor=\"@color/text_color\"\n                    android:textIsSelectable=\"true\"\n                    android:textSize=\"@dimen/text_font_size_content_14sp\"/>\n            </LinearLayout>\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"1px\"\n                android:background=\"@color/gray_ef\"/>\n\n            <LinearLayout\n                android:id=\"@+id/my_git_ll\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"48dp\"\n                android:background=\"@drawable/custom_ll_bg\"\n                android:clickable=\"true\"\n                android:gravity=\"center_vertical\"\n                >\n\n                <ImageView\n                    android:layout_width=\"28dp\"\n                    android:layout_height=\"28dp\"\n                    android:layout_marginLeft=\"@dimen/left_margin\"\n                    android:src=\"@mipmap/github_icon\"/>\n\n                <TextView\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_marginLeft=\"@dimen/left_margin_normal\"\n                    android:layout_weight=\"1\"\n                    android:gravity=\"center_vertical\"\n                    android:text=\"@string/my_github\"\n                    android:textColor=\"@color/text_color\"\n                    android:textSize=\"@dimen/text_font_size_content_14sp\"/>\n            </LinearLayout>\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"1px\"\n                android:background=\"@color/gray_ef\"/>\n\n            <LinearLayout\n                android:id=\"@+id/my_blog_ll\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"48dp\"\n                android:background=\"@drawable/custom_ll_bg\"\n                android:clickable=\"true\"\n                android:gravity=\"center_vertical\"\n                >\n\n                <ImageView\n                    android:layout_width=\"28dp\"\n                    android:layout_height=\"28dp\"\n                    android:layout_marginLeft=\"@dimen/left_margin\"\n                    android:src=\"@mipmap/blog_icon\"/>\n\n                <TextView\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_marginLeft=\"@dimen/left_margin_normal\"\n                    android:layout_weight=\"1\"\n                    android:gravity=\"center_vertical\"\n                    android:text=\"@string/my_blog\"\n                    android:textColor=\"@color/text_color\"\n                    android:textSize=\"@dimen/text_font_size_content_14sp\"/>\n            </LinearLayout>\n\n            <View\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"1px\"\n                android:background=\"@color/gray_ef\"/>\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"@dimen/top_margin\"\n                android:text=\"V 1.0.0\"\n                android:textColor=\"@color/text_color\"\n                android:textSize=\"@dimen/text_font_size_small_12sp\"/>\n        </LinearLayout>\n    </ScrollView>\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_home.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    >\n\n  <LinearLayout\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"?attr/actionBarSize\"\n      android:orientation=\"horizontal\"\n      >\n\n    <TextView\n        android:layout_width=\"0dp\"\n        android:layout_height=\"match_parent\"\n        android:layout_weight=\"1\"\n        android:background=\"@color/green\"\n        android:gravity=\"center_vertical|left\"\n        android:paddingLeft=\"@dimen/left_padding\"\n        android:text=\"@string/app_name\"\n        android:textColor=\"@color/text_color\"\n        android:textSize=\"@dimen/text_font_size_big_22sp\"\n        />\n\n    <ImageView\n        android:id=\"@+id/about_iv\"\n        android:layout_width=\"?attr/actionBarSize\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:background=\"@drawable/github_click_bg\"\n        android:padding=\"@dimen/normal_padding\"\n        android:src=\"@mipmap/about_icon\"\n        />\n\n  </LinearLayout>\n\n\n  <com.gu.library.OrientedViewPager\n      android:id=\"@+id/view_pager\"\n      android:layout_width=\"match_parent\"\n      android:layout_height=\"match_parent\"\n      android:background=\"@color/text_color\"\n      ></com.gu.library.OrientedViewPager>\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/activity_webview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"match_parent\"\n              android:orientation=\"vertical\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\">\n\n        <WebView\n            android:id=\"@+id/web_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n        </WebView>\n\n        <ProgressBar\n            android:id=\"@+id/web_view_progress_bar\"\n            style=\"@style/customProgressBar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"4dp\"\n            android:layout_alignParentTop=\"true\"\n            android:max=\"100\"/>\n    </RelativeLayout>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_card.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!-- 使用AndroidAutoLayout进行卡片适配 -->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n              xmlns:card_view=\"http://schemas.android.com/apk/res-auto\"\n              android:layout_width=\"match_parent\"\n              android:layout_height=\"match_parent\"\n              android:focusable=\"false\"\n              android:orientation=\"vertical\">\n\n    <android.support.v7.widget.CardView\n        android:id=\"@+id/card_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginBottom=\"@dimen/bottom_margin\"\n        android:layout_marginLeft=\"@dimen/left_margin\"\n        android:layout_marginRight=\"@dimen/right_margin\"\n        android:layout_marginTop=\"@dimen/top_margin\"\n        android:focusable=\"false\"\n        card_view:cardElevation=\"@dimen/card_elevation\">\n\n        <TextView\n            android:id=\"@+id/card_num_tv\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:gravity=\"center\"\n            android:textColor=\"@color/text_color\"\n            android:textSize=\"@dimen/text_font_size_big_60sp\"\n            android:textStyle=\"bold\"/>\n\n    </android.support.v7.widget.CardView>\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"green\">#00BA9C</color>\n    <color name=\"green_dark\">#05917a</color>\n    <color name=\"text_color\">#141d26</color>\n    <color name=\"transparent\">#00000000</color>\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"gray_ef\">#efefef</color>\n    <color name=\"progress_bar_color\">#00BA9C</color>\n</resources>"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- 字体大小 -->\n    <dimen name=\"text_font_size_big_60sp\">60sp</dimen>\n    <dimen name=\"text_font_size_big_22sp\">22sp</dimen>\n    <dimen name=\"text_font_size_title_18sp\">18sp</dimen>\n    <dimen name=\"text_font_size_title_16sp\">16sp</dimen>\n    <dimen name=\"text_font_size_content_14sp\">14sp</dimen>\n    <dimen name=\"text_font_size_small_12sp\">12sp</dimen>\n\n    <!-- 边距设置 -->\n    <dimen name=\"left_margin\">20dp</dimen>\n    <dimen name=\"right_margin\">20dp</dimen>\n    <dimen name=\"top_margin\">20dp</dimen>\n    <dimen name=\"bottom_margin\">40dp</dimen>\n    <dimen name=\"left_padding\">8dp</dimen>\n    <dimen name=\"normal_padding\">8dp</dimen>\n    <dimen name=\"left_margin_big\">10dp</dimen>\n    <dimen name=\"left_margin_normal\">8dp</dimen>\n    <dimen name=\"right_margin_big\">10dp</dimen>\n    <dimen name=\"right_margin_normal\">8dp</dimen>\n    <dimen name=\"top_margin_big\">10dp</dimen>\n    <dimen name=\"top_margin_normal\">8dp</dimen>\n    <dimen name=\"bottom_margin_big\">10dp</dimen>\n    <dimen name=\"bottom_margin_normal\">8dp</dimen>\n\n\n\n    <!-- cardview阴影-->\n    <dimen name=\"card_elevation\">10dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n  <string name=\"app_name\">CardStackViewpager</string>\n  <string name=\"my_name\">Nate Robinson</string>\n  <string name=\"my_qq\">840501291</string>\n  <string name=\"my_github\">https://github.com/NateRobinson</string>\n  <string name=\"my_blog\">http://blog.csdn.net/u011771755</string>\n  <string name=\"my_signature\">Never give up~</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n  <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n    <item name=\"colorPrimary\">@color/green</item>\n    <item name=\"colorPrimaryDark\">@color/green_dark</item>\n    <item name=\"colorAccent\">@color/green</item>\n  </style>\n\n\n  <style name=\"customProgressBar\" parent=\"@android:style/Widget.ProgressBar.Horizontal\">\n    <item name=\"android:maxHeight\">4.0dip</item>\n    <item name=\"android:indeterminateDrawable\">@drawable/progress_bar_h5</item>\n    <item name=\"android:progressDrawable\">@drawable/progress_bar_h5</item>\n    <item name=\"android:minHeight\">4.0dip</item>\n  </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/gu/cardstackviewpager/ExampleUnitTest.java",
    "content": "package com.gu.cardstackviewpager;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n  repositories {\n    google()\n    jcenter()\n    mavenCentral()\n  }\n  dependencies {\n    classpath 'com.android.tools.build:gradle:3.2.0'\n    // NOTE: Do not place your application dependencies here; they belong\n    // in the individual module build.gradle files\n  }\n}\n\nallprojects {\n  repositories {\n    google()\n    jcenter()\n    maven { url \"https://jitpack.io\" }\n  }\n}\n\ntask clean(type: Delete) {\n  delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Dec 28 10:00:20 PST 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "library/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "library/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 27\n    buildToolsVersion \"28.0.2\"\n\n    defaultConfig {\n        minSdkVersion 15\n        targetSdkVersion 27\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    testImplementation 'junit:junit:4.12'\n    implementation 'com.android.support:appcompat-v7:27.1.1'\n}\n"
  },
  {
    "path": "library/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in D:\\AndroidTools\\sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "library/src/androidTest/java/com/gu/library/ApplicationTest.java",
    "content": "package com.gu.library;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "library/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.gu.library\">\n</manifest>\n"
  },
  {
    "path": "library/src/main/java/com/gu/library/OrientedViewPager.java",
    "content": "/**\n * Copyright 2015 Bartosz Lipinski\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.gu.library;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.database.DataSetObserver;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.SystemClock;\nimport android.support.v4.view.AccessibilityDelegateCompat;\nimport android.support.v4.view.MotionEventCompat;\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.VelocityTrackerCompat;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.view.ViewConfigurationCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v4.view.accessibility.AccessibilityEventCompat;\nimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;\nimport android.support.v4.view.accessibility.AccessibilityRecordCompat;\nimport android.support.v4.widget.EdgeEffectCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.FocusFinder;\nimport android.view.Gravity;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\nimport android.view.SoundEffectConstants;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.accessibility.AccessibilityEvent;\nimport android.view.animation.Interpolator;\nimport android.widget.Scroller;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\n\n/**\n * Created by Bartosz Lipinski\n * Based on castorflex's VerticalViewPager (https://github.com/castorflex/VerticalViewPager)\n *\n * 03.05.15\n */\npublic class OrientedViewPager extends ViewGroup {\n\n  public enum Orientation {\n    VERTICAL, HORIZONTAL\n  }\n\n  private static final String TAG = \"ViewPager\";\n  private static final boolean DEBUG = false;\n\n  private static final boolean USE_CACHE = false;\n\n  private static final int DEFAULT_OFFSCREEN_PAGES = 1;\n  private static final int MAX_SETTLE_DURATION = 600; // ms\n  private static final int MIN_DISTANCE_FOR_FLING = 25; // dips\n\n  private static final int DEFAULT_GUTTER_SIZE = 16; // dips\n\n  private static final int MIN_FLING_VELOCITY = 400; // dips\n\n  private static final int[] LAYOUT_ATTRS = new int[] {\n      android.R.attr.layout_gravity\n  };\n\n  /**\n   * Used to track what the expected number of items in the adapter should be.\n   * If the app changes this when we don't expect it, we'll throw a big obnoxious exception.\n   */\n  private int mExpectedAdapterCount;\n\n  private static class ItemInfo {\n    Object object;\n    int position;\n    boolean scrolling;\n    float sizeFactor;\n    float offset;\n  }\n\n  private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>() {\n    @Override\n    public int compare(ItemInfo lhs, ItemInfo rhs) {\n      return lhs.position - rhs.position;\n    }\n  };\n\n  private static final Interpolator sInterpolator = new Interpolator() {\n    public float getInterpolation(float t) {\n      t -= 1.0f;\n      return t * t * t * t * t + 1.0f;\n    }\n  };\n\n  private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();\n  private final ItemInfo mTempItem = new ItemInfo();\n\n  private final Rect mTempRect = new Rect();\n\n  private Orientation mOrientation = Orientation.HORIZONTAL;\n\n  private PagerAdapter mAdapter;\n  private int mCurItem;   // Index of currently displayed page.\n  private int mRestoredCurItem = -1;\n  private Parcelable mRestoredAdapterState = null;\n  private ClassLoader mRestoredClassLoader = null;\n  private Scroller mScroller;\n  private PagerObserver mObserver;\n\n  private int mPageMargin;\n  private Drawable mMarginDrawable;\n  private int mTopLeftPageBounds;\n  private int mBottomRightPageBounds;\n\n  // Offsets of the first and last items, if known.\n  // Set during population, used to determine if we are at the beginning\n  // or end of the pager data set during touch scrolling.\n  private float mFirstOffset = -Float.MAX_VALUE;\n  private float mLastOffset = Float.MAX_VALUE;\n\n  private int mChildWidthMeasureSpec;\n  private int mChildHeightMeasureSpec;\n  private boolean mInLayout;\n\n  private boolean mScrollingCacheEnabled;\n\n  private boolean mPopulatePending;\n  private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;\n\n  private boolean mIsBeingDragged;\n  private boolean mIsUnableToDrag;\n  private boolean mIgnoreGutter;\n  private int mDefaultGutterSize;\n  private int mGutterSize;\n  private int mTouchSlop;\n  /**\n   * Position of the last motion event.\n   */\n  private float mLastMotionX;\n  private float mLastMotionY;\n  private float mInitialMotionX;\n  private float mInitialMotionY;\n  /**\n   * ID of the active pointer. This is used to retain consistency during\n   * drags/flings if multiple pointers are used.\n   */\n  private int mActivePointerId = INVALID_POINTER;\n  /**\n   * Sentinel value for no current active pointer.\n   * Used by {@link #mActivePointerId}.\n   */\n  private static final int INVALID_POINTER = -1;\n\n  /**\n   * Determines speed during touch scrolling\n   */\n  private VelocityTracker mVelocityTracker;\n  private int mMinimumVelocity;\n  private int mMaximumVelocity;\n  private int mFlingDistance;\n  private int mCloseEnough;\n\n  // If the pager is at least this close to its final position, complete the scroll\n  // on touch down and let the user interact with the content inside instead of\n  // \"catching\" the flinging pager.\n  private static final int CLOSE_ENOUGH = 2; // dp\n\n  private boolean mFakeDragging;\n  private long mFakeDragBeginTime;\n\n  private EdgeEffectCompat mTopLeftEdge;\n  private EdgeEffectCompat mRightBottomEdge;\n\n  private boolean mFirstLayout = true;\n  private boolean mNeedCalculatePageOffsets = false;\n  private boolean mCalledSuper;\n  private int mDecorChildCount;\n\n  private ViewPager.OnPageChangeListener mOnPageChangeListener;\n  private ViewPager.OnPageChangeListener mInternalPageChangeListener;\n  private OnAdapterChangeListener mAdapterChangeListener;\n  private ViewPager.PageTransformer mPageTransformer;\n  private Method mSetChildrenDrawingOrderEnabled;\n\n  private static final int DRAW_ORDER_DEFAULT = 0;\n  private static final int DRAW_ORDER_FORWARD = 1;\n  private static final int DRAW_ORDER_REVERSE = 2;\n  private int mDrawingOrder;\n  private ArrayList<View> mDrawingOrderedChildren;\n  private static final ViewPositionComparator sPositionComparator = new ViewPositionComparator();\n\n  /**\n   * Indicates that the pager is in an idle, settled state. The current page\n   * is fully in view and no animation is in progress.\n   */\n  public static final int SCROLL_STATE_IDLE = 0;\n\n  /**\n   * Indicates that the pager is currently being dragged by the user.\n   */\n  public static final int SCROLL_STATE_DRAGGING = 1;\n\n  /**\n   * Indicates that the pager is in the process of settling to a final position.\n   */\n  public static final int SCROLL_STATE_SETTLING = 2;\n\n  private final Runnable mEndScrollRunnable = new Runnable() {\n    public void run() {\n      setScrollState(SCROLL_STATE_IDLE);\n      populate();\n    }\n  };\n\n  private int mScrollState = SCROLL_STATE_IDLE;\n\n  /**\n   * Used internally to monitor when adapters are switched.\n   */\n  interface OnAdapterChangeListener {\n    public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAdapter);\n  }\n\n  /**\n   * Used internally to tag special types of child views that should be added as\n   * pager decorations by default.\n   */\n  interface Decor {\n  }\n\n  public OrientedViewPager(Context context) {\n    super(context);\n    initViewPager();\n  }\n\n  public OrientedViewPager(Context context, AttributeSet attrs) {\n    super(context, attrs);\n    initViewPager();\n  }\n\n  void initViewPager() {\n    setWillNotDraw(false);\n    setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);\n    setFocusable(true);\n    final Context context = getContext();\n    mScroller = new Scroller(context, sInterpolator);\n    final ViewConfiguration configuration = ViewConfiguration.get(context);\n    final float density = context.getResources().getDisplayMetrics().density;\n\n    mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);\n    mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);\n    mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();\n    mTopLeftEdge = new EdgeEffectCompat(context);\n    mRightBottomEdge = new EdgeEffectCompat(context);\n\n    mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);\n    mCloseEnough = (int) (CLOSE_ENOUGH * density);\n    mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);\n\n    ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate());\n\n    if (ViewCompat.getImportantForAccessibility(this)\n        == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {\n      ViewCompat.setImportantForAccessibility(this,\n          ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);\n    }\n  }\n\n  public void setOrientation(Orientation orientation) {\n    mOrientation = orientation;\n  }\n\n  @Override\n  protected void onDetachedFromWindow() {\n    removeCallbacks(mEndScrollRunnable);\n    super.onDetachedFromWindow();\n  }\n\n  private void setScrollState(int newState) {\n    if (mScrollState == newState) {\n      return;\n    }\n\n    mScrollState = newState;\n    if (mPageTransformer != null) {\n      // PageTransformers can do complex things that benefit from hardware layers.\n      enableLayers(newState != SCROLL_STATE_IDLE);\n    }\n    if (mOnPageChangeListener != null) {\n      mOnPageChangeListener.onPageScrollStateChanged(newState);\n    }\n  }\n\n  /**\n   * Set a PagerAdapter that will supply views for this pager as needed.\n   *\n   * @param adapter Adapter to use\n   */\n  public void setAdapter(PagerAdapter adapter) {\n    if (mAdapter != null) {\n      mAdapter.unregisterDataSetObserver(mObserver);\n      mAdapter.startUpdate(this);\n      for (int i = 0; i < mItems.size(); i++) {\n        final ItemInfo ii = mItems.get(i);\n        mAdapter.destroyItem(this, ii.position, ii.object);\n      }\n      mAdapter.finishUpdate(this);\n      mItems.clear();\n      removeNonDecorViews();\n      mCurItem = 0;\n      scrollTo(0, 0);\n    }\n\n    final PagerAdapter oldAdapter = mAdapter;\n    mAdapter = adapter;\n    mExpectedAdapterCount = 0;\n\n    if (mAdapter != null) {\n      if (mObserver == null) {\n        mObserver = new PagerObserver();\n      }\n      mAdapter.registerDataSetObserver(mObserver);\n      mPopulatePending = false;\n      final boolean wasFirstLayout = mFirstLayout;\n      mFirstLayout = true;\n      mExpectedAdapterCount = mAdapter.getCount();\n      if (mRestoredCurItem >= 0) {\n        mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);\n        setCurrentItemInternal(mRestoredCurItem, false, true);\n        mRestoredCurItem = -1;\n        mRestoredAdapterState = null;\n        mRestoredClassLoader = null;\n      } else if (!wasFirstLayout) {\n        populate();\n      } else {\n        requestLayout();\n      }\n    }\n\n    if (mAdapterChangeListener != null && oldAdapter != adapter) {\n      mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);\n    }\n  }\n\n  private void removeNonDecorViews() {\n    for (int i = 0; i < getChildCount(); i++) {\n      final View child = getChildAt(i);\n      final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n      if (!lp.isDecor) {\n        removeViewAt(i);\n        i--;\n      }\n    }\n  }\n\n  /**\n   * Retrieve the current adapter supplying pages.\n   *\n   * @return The currently registered PagerAdapter\n   */\n  public PagerAdapter getAdapter() {\n    return mAdapter;\n  }\n\n  void setOnAdapterChangeListener(OnAdapterChangeListener listener) {\n    mAdapterChangeListener = listener;\n  }\n\n  private int getClientSize() {\n    return (mOrientation == Orientation.VERTICAL) ?\n        getMeasuredHeight() - getPaddingTop() - getPaddingBottom() :\n        getMeasuredWidth() - getPaddingLeft() - getPaddingRight();\n  }\n\n  /**\n   * Set the currently selected page. If the ViewPager has already been through its first\n   * layout with its current adapter there will be a smooth animated transition between\n   * the current item and the specified item.\n   *\n   * @param item Item index to select\n   */\n  public void setCurrentItem(int item) {\n    mPopulatePending = false;\n    setCurrentItemInternal(item, !mFirstLayout, false);\n  }\n\n  /**\n   * Set the currently selected page.\n   *\n   * @param item Item index to select\n   * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately\n   */\n  public void setCurrentItem(int item, boolean smoothScroll) {\n    mPopulatePending = false;\n    setCurrentItemInternal(item, smoothScroll, false);\n  }\n\n  public int getCurrentItem() {\n    return mCurItem;\n  }\n\n  void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {\n    setCurrentItemInternal(item, smoothScroll, always, 0);\n  }\n\n  void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {\n    if (mAdapter == null || mAdapter.getCount() <= 0) {\n      setScrollingCacheEnabled(false);\n      return;\n    }\n    if (!always && mCurItem == item && mItems.size() != 0) {\n      setScrollingCacheEnabled(false);\n      return;\n    }\n\n    if (item < 0) {\n      item = 0;\n    } else if (item >= mAdapter.getCount()) {\n      item = mAdapter.getCount() - 1;\n    }\n    final int pageLimit = mOffscreenPageLimit;\n    if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {\n      // We are doing a jump by more than one page.  To avoid\n      // glitches, we want to keep all current pages in the view\n      // until the scroll ends.\n      for (int i = 0; i < mItems.size(); i++) {\n        mItems.get(i).scrolling = true;\n      }\n    }\n    final boolean dispatchSelected = mCurItem != item;\n\n    if (mFirstLayout) {\n      // We don't have any idea how big we are yet and shouldn't have any pages either.\n      // Just set things up and let the pending layout handle things.\n      mCurItem = item;\n      if (dispatchSelected && mOnPageChangeListener != null) {\n        mOnPageChangeListener.onPageSelected(item);\n      }\n      if (dispatchSelected && mInternalPageChangeListener != null) {\n        mInternalPageChangeListener.onPageSelected(item);\n      }\n      requestLayout();\n    } else {\n      populate(item);\n      scrollToItem(item, smoothScroll, velocity, dispatchSelected);\n    }\n  }\n\n  private void scrollToItem(int item, boolean smoothScroll, int velocity,\n      boolean dispatchSelected) {\n    final ItemInfo curInfo = infoForPosition(item);\n    int dest = 0;\n    if (curInfo != null) {\n      final int size = getClientSize();\n      dest = (int) (size * Math.max(mFirstOffset,\n          Math.min(curInfo.offset, mLastOffset)));\n    }\n    if (smoothScroll) {\n      if (mOrientation == Orientation.VERTICAL) {\n        smoothScrollTo(0, dest, velocity);\n      } else {\n        smoothScrollTo(dest, 0, velocity);\n      }\n      if (dispatchSelected && mOnPageChangeListener != null) {\n        mOnPageChangeListener.onPageSelected(item);\n      }\n      if (dispatchSelected && mInternalPageChangeListener != null) {\n        mInternalPageChangeListener.onPageSelected(item);\n      }\n    } else {\n      if (dispatchSelected && mOnPageChangeListener != null) {\n        mOnPageChangeListener.onPageSelected(item);\n      }\n      if (dispatchSelected && mInternalPageChangeListener != null) {\n        mInternalPageChangeListener.onPageSelected(item);\n      }\n      completeScroll(false);\n      if (mOrientation == Orientation.VERTICAL) {\n        scrollTo(0, dest);\n      } else {\n        scrollTo(dest, 0);\n      }\n      pageScrolled(dest);\n    }\n  }\n\n  /**\n   * Set a listener that will be invoked whenever the page changes or is incrementally\n   * scrolled. See {@link android.support.v4.view.ViewPager.OnPageChangeListener}.\n   *\n   * @param listener Listener to set\n   */\n  public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {\n    mOnPageChangeListener = listener;\n  }\n\n  /**\n   * Set a {@link android.support.v4.view.ViewPager.PageTransformer} that will be called for each\n   * attached page whenever\n   * the scroll position is changed. This allows the application to apply custom property\n   * transformations to each page, overriding the default sliding look and feel.\n   * <p/>\n   * <p><em>Note:</em> Prior to Android 3.0 the property animation APIs did not exist.\n   * As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.</p>\n   *\n   * @param reverseDrawingOrder true if the supplied PageTransformer requires page views\n   * to be drawn from last to first instead of first to last.\n   * @param transformer PageTransformer that will modify each page's animation properties\n   */\n  public void setPageTransformer(boolean reverseDrawingOrder,\n      ViewPager.PageTransformer transformer) {\n    if (Build.VERSION.SDK_INT >= 11) {\n      final boolean hasTransformer = transformer != null;\n      final boolean needsPopulate = hasTransformer != (mPageTransformer != null);\n      mPageTransformer = transformer;\n      setChildrenDrawingOrderEnabledCompat(hasTransformer);\n      if (hasTransformer) {\n        mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;\n      } else {\n        mDrawingOrder = DRAW_ORDER_DEFAULT;\n      }\n      if (needsPopulate) populate();\n    }\n  }\n\n  void setChildrenDrawingOrderEnabledCompat(boolean enable) {\n    if (Build.VERSION.SDK_INT >= 7) {\n      if (mSetChildrenDrawingOrderEnabled == null) {\n        try {\n          mSetChildrenDrawingOrderEnabled = ViewGroup.class.getDeclaredMethod(\n              \"setChildrenDrawingOrderEnabled\", new Class[] { Boolean.TYPE });\n        } catch (NoSuchMethodException e) {\n          Log.e(TAG, \"Can't find setChildrenDrawingOrderEnabled\", e);\n        }\n      }\n      try {\n        mSetChildrenDrawingOrderEnabled.invoke(this, enable);\n      } catch (Exception e) {\n        Log.e(TAG, \"Error changing children drawing order\", e);\n      }\n    }\n  }\n\n  @Override\n  protected int getChildDrawingOrder(int childCount, int i) {\n    final int index = mDrawingOrder == DRAW_ORDER_REVERSE ? childCount - 1 - i : i;\n    final int result =\n        ((LayoutParams) mDrawingOrderedChildren.get(index).getLayoutParams()).childIndex;\n    return result;\n  }\n\n  /**\n   * Set a separate OnPageChangeListener for internal use by the support library.\n   *\n   * @param listener Listener to set\n   * @return The old listener that was set, if any.\n   */\n  ViewPager.OnPageChangeListener setInternalPageChangeListener(\n      ViewPager.OnPageChangeListener listener) {\n    ViewPager.OnPageChangeListener oldListener = mInternalPageChangeListener;\n    mInternalPageChangeListener = listener;\n    return oldListener;\n  }\n\n  /**\n   * Returns the number of pages that will be retained to either side of the\n   * current page in the view hierarchy in an idle state. Defaults to 1.\n   *\n   * @return How many pages will be kept offscreen on either side\n   * @see #setOffscreenPageLimit(int)\n   */\n  public int getOffscreenPageLimit() {\n    return mOffscreenPageLimit;\n  }\n\n  /**\n   * Set the number of pages that should be retained to either side of the\n   * current page in the view hierarchy in an idle state. Pages beyond this\n   * limit will be recreated from the adapter when needed.\n   * <p/>\n   * <p>This is offered as an optimization. If you know in advance the number\n   * of pages you will need to support or have lazy-loading mechanisms in place\n   * on your pages, tweaking this setting can have benefits in perceived smoothness\n   * of paging animations and interaction. If you have a small number of pages (3-4)\n   * that you can keep active all at once, less time will be spent in layout for\n   * newly created view subtrees as the user pages back and forth.</p>\n   * <p/>\n   * <p>You should keep this limit low, especially if your pages have complex layouts.\n   * This setting defaults to 1.</p>\n   *\n   * @param limit How many pages will be kept offscreen in an idle state.\n   */\n  public void setOffscreenPageLimit(int limit) {\n    if (limit < DEFAULT_OFFSCREEN_PAGES) {\n      Log.w(TAG, \"Requested offscreen page limit \" + limit + \" too small; defaulting to \" +\n          DEFAULT_OFFSCREEN_PAGES);\n      limit = DEFAULT_OFFSCREEN_PAGES;\n    }\n    if (limit != mOffscreenPageLimit) {\n      mOffscreenPageLimit = limit;\n      populate();\n    }\n  }\n\n  /**\n   * Set the margin between pages.\n   *\n   * @param marginPixels Distance between adjacent pages in pixels\n   * @see #getPageMargin()\n   * @see #setPageMarginDrawable(Drawable)\n   * @see #setPageMarginDrawable(int)\n   */\n  public void setPageMargin(int marginPixels) {\n    final int oldMargin = mPageMargin;\n    mPageMargin = marginPixels;\n\n    final int size = (mOrientation == Orientation.VERTICAL) ? getHeight() : getWidth();\n    recomputeScrollPosition(size, size, marginPixels, oldMargin);\n\n    requestLayout();\n  }\n\n  /**\n   * Return the margin between pages.\n   *\n   * @return The size of the margin in pixels\n   */\n  public int getPageMargin() {\n    return mPageMargin;\n  }\n\n  /**\n   * Set a drawable that will be used to fill the margin between pages.\n   *\n   * @param d Drawable to display between pages\n   */\n  public void setPageMarginDrawable(Drawable d) {\n    mMarginDrawable = d;\n    if (d != null) refreshDrawableState();\n    setWillNotDraw(d == null);\n    invalidate();\n  }\n\n  /**\n   * Set a drawable that will be used to fill the margin between pages.\n   *\n   * @param resId Resource ID of a drawable to display between pages\n   */\n  public void setPageMarginDrawable(int resId) {\n    setPageMarginDrawable(getContext().getResources().getDrawable(resId));\n  }\n\n  @Override\n  protected boolean verifyDrawable(Drawable who) {\n    return super.verifyDrawable(who) || who == mMarginDrawable;\n  }\n\n  @Override\n  protected void drawableStateChanged() {\n    super.drawableStateChanged();\n    final Drawable d = mMarginDrawable;\n    if (d != null && d.isStateful()) {\n      d.setState(getDrawableState());\n    }\n  }\n\n  // We want the duration of the page snap animation to be influenced by the distance that\n  // the screen has to travel, however, we don't want this duration to be effected in a\n  // purely linear fashion. Instead, we use this method to moderate the effect that the distance\n  // of travel has on the overall snap duration.\n  float distanceInfluenceForSnapDuration(float f) {\n    f -= 0.5f; // center the values about 0.\n    f *= 0.3f * Math.PI / 2.0f;\n    return (float) Math.sin(f);\n  }\n\n  /**\n   * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.\n   *\n   * @param x the number of pixels to scroll by on the X axis\n   * @param y the number of pixels to scroll by on the Y axis\n   */\n  void smoothScrollTo(int x, int y) {\n    smoothScrollTo(x, y, 0);\n  }\n\n  /**\n   * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.\n   *\n   * @param x the number of pixels to scroll by on the X axis\n   * @param y the number of pixels to scroll by on the Y axis\n   * @param velocity the velocity associated with a fling, if applicable. (0 otherwise)\n   */\n  void smoothScrollTo(int x, int y, int velocity) {\n    if (getChildCount() == 0) {\n      // Nothing to do.\n      setScrollingCacheEnabled(false);\n      return;\n    }\n    int sx = getScrollX();\n    int sy = getScrollY();\n    int dx = x - sx;\n    int dy = y - sy;\n    if (dx == 0 && dy == 0) {\n      completeScroll(false);\n      populate();\n      setScrollState(SCROLL_STATE_IDLE);\n      return;\n    }\n\n    setScrollingCacheEnabled(true);\n    setScrollState(SCROLL_STATE_SETTLING);\n\n    final int size = getClientSize();\n    final int halfSize = size / 2;\n    final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / size);\n    final float distance = halfSize + halfSize *\n        distanceInfluenceForSnapDuration(distanceRatio);\n\n    int duration = 0;\n    velocity = Math.abs(velocity);\n    if (velocity > 0) {\n      duration = 4 * Math.round(1000 * Math.abs(distance / velocity));\n    } else {\n      final float pageSize = size * mAdapter.getPageWidth(mCurItem);\n      final float pageDelta = (float) Math.abs(dx) / (pageSize + mPageMargin);\n      duration = (int) ((pageDelta + 1) * 100);\n    }\n    duration = Math.min(duration, MAX_SETTLE_DURATION);\n\n    mScroller.startScroll(sx, sy, dx, dy, duration);\n    ViewCompat.postInvalidateOnAnimation(this);\n  }\n\n  ItemInfo addNewItem(int position, int index) {\n    ItemInfo ii = new ItemInfo();\n    ii.position = position;\n    ii.object = mAdapter.instantiateItem(this, position);\n    ii.sizeFactor = mAdapter.getPageWidth(position);\n    if (index < 0 || index >= mItems.size()) {\n      mItems.add(ii);\n    } else {\n      mItems.add(index, ii);\n    }\n    return ii;\n  }\n\n  void dataSetChanged() {\n    // This method only gets called if our observer is attached, so mAdapter is non-null.\n\n    final int adapterCount = mAdapter.getCount();\n    mExpectedAdapterCount = adapterCount;\n    boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 &&\n        mItems.size() < adapterCount;\n    int newCurrItem = mCurItem;\n\n    boolean isUpdating = false;\n    for (int i = 0; i < mItems.size(); i++) {\n      final ItemInfo ii = mItems.get(i);\n      final int newPos = mAdapter.getItemPosition(ii.object);\n\n      if (newPos == PagerAdapter.POSITION_UNCHANGED) {\n        continue;\n      }\n\n      if (newPos == PagerAdapter.POSITION_NONE) {\n        mItems.remove(i);\n        i--;\n\n        if (!isUpdating) {\n          mAdapter.startUpdate(this);\n          isUpdating = true;\n        }\n\n        mAdapter.destroyItem(this, ii.position, ii.object);\n        needPopulate = true;\n\n        if (mCurItem == ii.position) {\n          // Keep the current item in the valid range\n          newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));\n          needPopulate = true;\n        }\n        continue;\n      }\n\n      if (ii.position != newPos) {\n        if (ii.position == mCurItem) {\n          // Our current item changed position. Follow it.\n          newCurrItem = newPos;\n        }\n\n        ii.position = newPos;\n        needPopulate = true;\n      }\n    }\n\n    if (isUpdating) {\n      mAdapter.finishUpdate(this);\n    }\n\n    Collections.sort(mItems, COMPARATOR);\n\n    if (needPopulate) {\n      // Reset our known page widths; populate will recompute them.\n      final int childCount = getChildCount();\n      for (int i = 0; i < childCount; i++) {\n        final View child = getChildAt(i);\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        if (!lp.isDecor) {\n          lp.heightFactor = 0.f;\n        }\n      }\n\n      setCurrentItemInternal(newCurrItem, false, true);\n      requestLayout();\n    }\n  }\n\n  void populate() {\n    populate(mCurItem);\n  }\n\n  void populate(int newCurrentItem) {\n    ItemInfo oldCurInfo = null;\n    int focusDirection = View.FOCUS_FORWARD;\n    if (mCurItem != newCurrentItem) {\n      focusDirection = mCurItem < newCurrentItem ? View.FOCUS_DOWN : View.FOCUS_UP;\n      oldCurInfo = infoForPosition(mCurItem);\n      mCurItem = newCurrentItem;\n    }\n\n    if (mAdapter == null) {\n      sortChildDrawingOrder();\n      return;\n    }\n\n    // Bail now if we are waiting to populate.  This is to hold off\n    // on creating views from the time the user releases their finger to\n    // fling to a new position until we have finished the scroll to\n    // that position, avoiding glitches from happening at that point.\n    if (mPopulatePending) {\n      if (DEBUG) Log.i(TAG, \"populate is pending, skipping for now...\");\n      sortChildDrawingOrder();\n      return;\n    }\n\n    // Also, don't populate until we are attached to a window.  This is to\n    // avoid trying to populate before we have restored our view hierarchy\n    // state and conflicting with what is restored.\n    if (getWindowToken() == null) {\n      return;\n    }\n\n    mAdapter.startUpdate(this);\n\n    final int pageLimit = mOffscreenPageLimit;\n    final int startPos = Math.max(0, mCurItem - pageLimit);\n    final int N = mAdapter.getCount();\n    final int endPos = Math.min(N - 1, mCurItem + pageLimit);\n\n    if (N != mExpectedAdapterCount) {\n      String resName;\n      try {\n        resName = getResources().getResourceName(getId());\n      } catch (Resources.NotFoundException e) {\n        resName = Integer.toHexString(getId());\n      }\n      throw new IllegalStateException(\"The application's PagerAdapter changed the adapter's\" +\n          \" contents without calling PagerAdapter#notifyDataSetChanged!\" +\n          \" Expected adapter item count: \" + mExpectedAdapterCount + \", found: \" + N +\n          \" Pager id: \" + resName +\n          \" Pager class: \" + getClass() +\n          \" Problematic adapter: \" + mAdapter.getClass());\n    }\n\n    // Locate the currently focused item or add it if needed.\n    int curIndex = -1;\n    ItemInfo curItem = null;\n    for (curIndex = 0; curIndex < mItems.size(); curIndex++) {\n      final ItemInfo ii = mItems.get(curIndex);\n      if (ii.position >= mCurItem) {\n        if (ii.position == mCurItem) curItem = ii;\n        break;\n      }\n    }\n\n    if (curItem == null && N > 0) {\n      curItem = addNewItem(mCurItem, curIndex);\n    }\n\n    // Fill 3x the available width or up to the number of offscreen\n    // pages requested to either side, whichever is larger.\n    // If we have no current item we have no work to do.\n    if (curItem != null) {\n      float extraSizeTopLeft = 0.f;\n      int itemIndex = curIndex - 1;\n      ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\n      final int clientSize = getClientSize();\n      final float topLeftSizeNeeded = clientSize <= 0 ? 0 :\n          2.f - curItem.sizeFactor + (float) getPaddingLeft() / (float) clientSize;\n      for (int pos = mCurItem - 1; pos >= 0; pos--) {\n        if (extraSizeTopLeft >= topLeftSizeNeeded && pos < startPos) {\n          if (ii == null) {\n            break;\n          }\n          if (pos == ii.position && !ii.scrolling) {\n            mItems.remove(itemIndex);\n            mAdapter.destroyItem(this, pos, ii.object);\n            if (DEBUG) {\n              Log.i(TAG, \"populate() - destroyItem() with pos: \" + pos +\n                  \" view: \" + ((View) ii.object));\n            }\n            itemIndex--;\n            curIndex--;\n            ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\n          }\n        } else if (ii != null && pos == ii.position) {\n          extraSizeTopLeft += ii.sizeFactor;\n          itemIndex--;\n          ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\n        } else {\n          ii = addNewItem(pos, itemIndex + 1);\n          extraSizeTopLeft += ii.sizeFactor;\n          curIndex++;\n          ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;\n        }\n      }\n\n      float extraSizeBottomRight = curItem.sizeFactor;\n      itemIndex = curIndex + 1;\n      if (extraSizeBottomRight < 2.f) {\n        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\n        final float bottomRightSizeNeeded = clientSize <= 0 ? 0 :\n            (float) getPaddingRight() / (float) clientSize + 2.f;\n        for (int pos = mCurItem + 1; pos < N; pos++) {\n          if (extraSizeBottomRight >= bottomRightSizeNeeded && pos > endPos) {\n            if (ii == null) {\n              break;\n            }\n            if (pos == ii.position && !ii.scrolling) {\n              mItems.remove(itemIndex);\n              mAdapter.destroyItem(this, pos, ii.object);\n              if (DEBUG) {\n                Log.i(TAG, \"populate() - destroyItem() with pos: \" + pos +\n                    \" view: \" + ((View) ii.object));\n              }\n              ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\n            }\n          } else if (ii != null && pos == ii.position) {\n            extraSizeBottomRight += ii.sizeFactor;\n            itemIndex++;\n            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\n          } else {\n            ii = addNewItem(pos, itemIndex);\n            itemIndex++;\n            extraSizeBottomRight += ii.sizeFactor;\n            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;\n          }\n        }\n      }\n\n      calculatePageOffsets(curItem, curIndex, oldCurInfo);\n    }\n\n    if (DEBUG) {\n      Log.i(TAG, \"Current page list:\");\n      for (int i = 0; i < mItems.size(); i++) {\n        Log.i(TAG, \"#\" + i + \": page \" + mItems.get(i).position);\n      }\n    }\n\n    mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);\n\n    mAdapter.finishUpdate(this);\n\n    // Check width measurement of current pages and drawing sort order.\n    // Update LayoutParams as needed.\n    final int childCount = getChildCount();\n    if (mOrientation == Orientation.VERTICAL) {\n      for (int i = 0; i < childCount; i++) {\n        final View child = getChildAt(i);\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        lp.childIndex = i;\n        if (!lp.isDecor && lp.heightFactor == 0.f) {\n          // 0 means requery the adapter for this, it doesn't have a valid width\n          // .\n          final ItemInfo ii = infoForChild(child);\n          if (ii != null) {\n            lp.heightFactor = ii.sizeFactor;\n            lp.position = ii.position;\n          }\n        }\n      }\n    } else {\n      for (int i = 0; i < childCount; i++) {\n        final View child = getChildAt(i);\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        lp.childIndex = i;\n        if (!lp.isDecor && lp.widthFactor == 0.f) {\n          // 0 means requery the adapter for this, it doesn't have a valid width.\n          final ItemInfo ii = infoForChild(child);\n          if (ii != null) {\n            lp.widthFactor = ii.sizeFactor;\n            lp.position = ii.position;\n          }\n        }\n      }\n    }\n    sortChildDrawingOrder();\n\n    if (hasFocus()) {\n      View currentFocused = findFocus();\n      ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;\n      if (ii == null || ii.position != mCurItem) {\n        for (int i = 0; i < getChildCount(); i++) {\n          View child = getChildAt(i);\n          ii = infoForChild(child);\n          if (ii != null && ii.position == mCurItem) {\n            if (child.requestFocus(focusDirection)) {\n              break;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  private void sortChildDrawingOrder() {\n    if (mDrawingOrder != DRAW_ORDER_DEFAULT) {\n      if (mDrawingOrderedChildren == null) {\n        mDrawingOrderedChildren = new ArrayList<View>();\n      } else {\n        mDrawingOrderedChildren.clear();\n      }\n      final int childCount = getChildCount();\n      for (int i = 0; i < childCount; i++) {\n        final View child = getChildAt(i);\n        mDrawingOrderedChildren.add(child);\n      }\n      Collections.sort(mDrawingOrderedChildren, sPositionComparator);\n    }\n  }\n\n  private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {\n    final int N = mAdapter.getCount();\n    final int size = getClientSize();\n    final float marginOffset = size > 0 ? (float) mPageMargin / size : 0;\n    // Fix up offsets for later layout.\n    if (oldCurInfo != null) {\n      final int oldCurPosition = oldCurInfo.position;\n      // Base offsets off of oldCurInfo.\n      if (oldCurPosition < curItem.position) {\n        int itemIndex = 0;\n        ItemInfo ii = null;\n        float offset = oldCurInfo.offset + oldCurInfo.sizeFactor + marginOffset;\n        for (int pos = oldCurPosition + 1;\n            pos <= curItem.position && itemIndex < mItems.size(); pos++) {\n          ii = mItems.get(itemIndex);\n          while (pos > ii.position && itemIndex < mItems.size() - 1) {\n            itemIndex++;\n            ii = mItems.get(itemIndex);\n          }\n          while (pos < ii.position) {\n            // We don't have an item populated for this,\n            // ask the adapter for an offset.\n            offset += mAdapter.getPageWidth(pos) + marginOffset;\n            pos++;\n          }\n          ii.offset = offset;\n          offset += ii.sizeFactor + marginOffset;\n        }\n      } else if (oldCurPosition > curItem.position) {\n        int itemIndex = mItems.size() - 1;\n        ItemInfo ii = null;\n        float offset = oldCurInfo.offset;\n        for (int pos = oldCurPosition - 1;\n            pos >= curItem.position && itemIndex >= 0; pos--) {\n          ii = mItems.get(itemIndex);\n          while (pos < ii.position && itemIndex > 0) {\n            itemIndex--;\n            ii = mItems.get(itemIndex);\n          }\n          while (pos > ii.position) {\n            // We don't have an item populated for this,\n            // ask the adapter for an offset.\n            offset -= mAdapter.getPageWidth(pos) + marginOffset;\n            pos--;\n          }\n          offset -= ii.sizeFactor + marginOffset;\n          ii.offset = offset;\n        }\n      }\n    }\n\n    // Base all offsets off of curItem.\n    final int itemCount = mItems.size();\n    float offset = curItem.offset;\n    int pos = curItem.position - 1;\n    mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;\n    mLastOffset = curItem.position == N - 1 ?\n        curItem.offset + curItem.sizeFactor - 1 : Float.MAX_VALUE;\n    // Previous pages\n    for (int i = curIndex - 1; i >= 0; i--, pos--) {\n      final ItemInfo ii = mItems.get(i);\n      while (pos > ii.position) {\n        offset -= mAdapter.getPageWidth(pos--) + marginOffset;\n      }\n      offset -= ii.sizeFactor + marginOffset;\n      ii.offset = offset;\n      if (ii.position == 0) mFirstOffset = offset;\n    }\n    offset = curItem.offset + curItem.sizeFactor + marginOffset;\n    pos = curItem.position + 1;\n    // Next pages\n    for (int i = curIndex + 1; i < itemCount; i++, pos++) {\n      final ItemInfo ii = mItems.get(i);\n      while (pos < ii.position) {\n        offset += mAdapter.getPageWidth(pos++) + marginOffset;\n      }\n      if (ii.position == N - 1) {\n        mLastOffset = offset + ii.sizeFactor - 1;\n      }\n      ii.offset = offset;\n      offset += ii.sizeFactor + marginOffset;\n    }\n\n    mNeedCalculatePageOffsets = false;\n  }\n\n  /**\n   * This is the persistent state that is saved by ViewPager.  Only needed\n   * if you are creating a sublass of ViewPager that must save its own\n   * state, in which case it should implement a subclass of this which\n   * contains that state.\n   */\n  public static class ViewPagerSavedState extends BaseSavedState {\n    int position;\n    Parcelable adapterState;\n    ClassLoader loader;\n\n    public ViewPagerSavedState(Parcelable superState) {\n      super(superState);\n    }\n\n    @Override\n    public void writeToParcel(Parcel out, int flags) {\n      super.writeToParcel(out, flags);\n      out.writeInt(position);\n      out.writeParcelable(adapterState, flags);\n    }\n\n    @Override\n    public String toString() {\n      return \"FragmentPager.SavedState{\"\n          + Integer.toHexString(System.identityHashCode(this))\n          + \" position=\" + position + \"}\";\n    }\n\n    public static final Parcelable.Creator<ViewPagerSavedState> CREATOR\n        = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<ViewPagerSavedState>() {\n      @Override\n      public ViewPagerSavedState createFromParcel(Parcel in, ClassLoader loader) {\n        return new ViewPagerSavedState(in, loader);\n      }\n\n      @Override\n      public ViewPagerSavedState[] newArray(int size) {\n        return new ViewPagerSavedState[size];\n      }\n    });\n\n    ViewPagerSavedState(Parcel in, ClassLoader loader) {\n      super(in);\n      if (loader == null) {\n        loader = getClass().getClassLoader();\n      }\n      position = in.readInt();\n      adapterState = in.readParcelable(loader);\n      this.loader = loader;\n    }\n  }\n\n  @Override\n  public Parcelable onSaveInstanceState() {\n    Parcelable superState = super.onSaveInstanceState();\n    ViewPagerSavedState ss = new ViewPagerSavedState(superState);\n    ss.position = mCurItem;\n    if (mAdapter != null) {\n      ss.adapterState = mAdapter.saveState();\n    }\n    return ss;\n  }\n\n  @Override\n  public void onRestoreInstanceState(Parcelable state) {\n    if (!(state instanceof ViewPagerSavedState)) {\n      super.onRestoreInstanceState(state);\n      return;\n    }\n\n    ViewPagerSavedState ss = (ViewPagerSavedState) state;\n    super.onRestoreInstanceState(ss.getSuperState());\n\n    if (mAdapter != null) {\n      mAdapter.restoreState(ss.adapterState, ss.loader);\n      setCurrentItemInternal(ss.position, false, true);\n    } else {\n      mRestoredCurItem = ss.position;\n      mRestoredAdapterState = ss.adapterState;\n      mRestoredClassLoader = ss.loader;\n    }\n  }\n\n  @Override\n  public void addView(View child, int index, ViewGroup.LayoutParams params) {\n    if (!checkLayoutParams(params)) {\n      params = generateLayoutParams(params);\n    }\n    final LayoutParams lp = (LayoutParams) params;\n    lp.isDecor |= child instanceof Decor;\n    if (mInLayout) {\n      if (lp != null && lp.isDecor) {\n        throw new IllegalStateException(\"Cannot add pager decor view during layout\");\n      }\n      lp.needsMeasure = true;\n      addViewInLayout(child, index, params);\n    } else {\n      super.addView(child, index, params);\n    }\n\n    if (USE_CACHE) {\n      if (child.getVisibility() != GONE) {\n        child.setDrawingCacheEnabled(mScrollingCacheEnabled);\n      } else {\n        child.setDrawingCacheEnabled(false);\n      }\n    }\n  }\n\n  @Override\n  public void removeView(View view) {\n    if (mInLayout) {\n      removeViewInLayout(view);\n    } else {\n      super.removeView(view);\n    }\n  }\n\n  ItemInfo infoForChild(View child) {\n    for (int i = 0; i < mItems.size(); i++) {\n      ItemInfo ii = mItems.get(i);\n      if (mAdapter.isViewFromObject(child, ii.object)) {\n        return ii;\n      }\n    }\n    return null;\n  }\n\n  ItemInfo infoForAnyChild(View child) {\n    ViewParent parent;\n    while ((parent = child.getParent()) != this) {\n      if (parent == null || !(parent instanceof View)) {\n        return null;\n      }\n      child = (View) parent;\n    }\n    return infoForChild(child);\n  }\n\n  ItemInfo infoForPosition(int position) {\n    for (int i = 0; i < mItems.size(); i++) {\n      ItemInfo ii = mItems.get(i);\n      if (ii.position == position) {\n        return ii;\n      }\n    }\n    return null;\n  }\n\n  @Override\n  protected void onAttachedToWindow() {\n    super.onAttachedToWindow();\n    mFirstLayout = true;\n  }\n\n  @Override\n  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n    // For simple implementation, our internal size is always 0.\n    // We depend on the container to specify the layout size of\n    // our view.  We can't really know what it is since we will be\n    // adding and removing different arbitrary views and do not\n    // want the layout to change as this happens.\n    setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),\n        getDefaultSize(0, heightMeasureSpec));\n\n    final int measuredSize =\n        (mOrientation == Orientation.VERTICAL) ? getMeasuredHeight() : getMeasuredWidth();\n    final int maxGutterSize = measuredSize / 10;\n    mGutterSize = Math.min(maxGutterSize, mDefaultGutterSize);\n\n    // Children are just made to fill our space.\n    int childWidthSize;\n    int childHeightSize;\n\n    if (mOrientation == Orientation.VERTICAL) {\n      childWidthSize = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();\n      childHeightSize = measuredSize - getPaddingTop() - getPaddingBottom();\n    } else {\n      childWidthSize = measuredSize - getPaddingLeft() - getPaddingRight();\n      childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();\n    }\n\n    /*\n     * Make sure all children have been properly measured. Decor views first.\n     * Right now we cheat and make this less complicated by assuming decor\n     * views won't intersect. We will pin to edges based on gravity.\n     */\n    int size = getChildCount();\n    for (int i = 0; i < size; ++i) {\n      final View child = getChildAt(i);\n      if (child.getVisibility() != GONE) {\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        if (lp != null && lp.isDecor) {\n          final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;\n          final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;\n          int widthMode = MeasureSpec.AT_MOST;\n          int heightMode = MeasureSpec.AT_MOST;\n          boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;\n          boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;\n\n          if (consumeVertical) {\n            widthMode = MeasureSpec.EXACTLY;\n          } else if (consumeHorizontal) {\n            heightMode = MeasureSpec.EXACTLY;\n          }\n\n          int widthSize = childWidthSize;\n          int heightSize = childHeightSize;\n          if (lp.width != LayoutParams.WRAP_CONTENT) {\n            widthMode = MeasureSpec.EXACTLY;\n            if (lp.width != LayoutParams.FILL_PARENT) {\n              widthSize = lp.width;\n            }\n          }\n          if (lp.height != LayoutParams.WRAP_CONTENT) {\n            heightMode = MeasureSpec.EXACTLY;\n            if (lp.height != LayoutParams.FILL_PARENT) {\n              heightSize = lp.height;\n            }\n          }\n          final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);\n          final int heightSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);\n          child.measure(widthSpec, heightSpec);\n\n          if (consumeVertical) {\n            childHeightSize -= child.getMeasuredHeight();\n          } else if (consumeHorizontal) {\n            childWidthSize -= child.getMeasuredWidth();\n          }\n        }\n      }\n    }\n\n    mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);\n    mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);\n\n    // Make sure we have created all fragments that we need to have shown.\n    mInLayout = true;\n    populate();\n    mInLayout = false;\n\n    // Page views next.\n    size = getChildCount();\n    for (int i = 0; i < size; ++i) {\n      final View child = getChildAt(i);\n      if (child.getVisibility() != GONE) {\n        if (DEBUG) {\n          Log.v(TAG, \"Measuring #\" + i + \" \" + child\n              + \": \" + mChildWidthMeasureSpec);\n        }\n\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        if (lp == null || !lp.isDecor) {\n          if (mOrientation == Orientation.VERTICAL) {\n            final int heightSpec = MeasureSpec.makeMeasureSpec(\n                (int) (childHeightSize * lp.heightFactor), MeasureSpec.EXACTLY);\n            child.measure(mChildWidthMeasureSpec, heightSpec);\n          } else {\n\n            final int widthSpec = MeasureSpec.makeMeasureSpec(\n                (int) (childWidthSize * lp.widthFactor), MeasureSpec.EXACTLY);\n            child.measure(widthSpec, mChildHeightMeasureSpec);\n          }\n        }\n      }\n    }\n  }\n\n  @Override\n  protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n    super.onSizeChanged(w, h, oldw, oldh);\n\n    // Make sure scroll position is set correctly.\n    if (mOrientation == Orientation.VERTICAL) {\n      if (h != oldh) {\n        recomputeScrollPosition(h, oldh, mPageMargin, mPageMargin);\n      }\n    } else {\n      if (w != oldw) {\n        recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);\n      }\n    }\n  }\n\n  private void recomputeScrollPosition(int size, int oldSize, int margin, int oldMargin) {\n    if (mOrientation == Orientation.VERTICAL) {\n      if (oldSize > 0 && !mItems.isEmpty()) {\n        final int heightWithMargin = size - getPaddingTop() - getPaddingBottom() + margin;\n        final int oldHeightWithMargin = oldSize - getPaddingTop() - getPaddingBottom()\n            + oldMargin;\n        final int ypos = getScrollY();\n        final float pageOffset = (float) ypos / oldHeightWithMargin;\n        final int newOffsetPixels = (int) (pageOffset * heightWithMargin);\n\n        scrollTo(getScrollX(), newOffsetPixels);\n        if (!mScroller.isFinished()) {\n          // We now return to your regularly scheduled scroll, already in progress.\n          final int newDuration = mScroller.getDuration() - mScroller.timePassed();\n          ItemInfo targetInfo = infoForPosition(mCurItem);\n          mScroller.startScroll(0, newOffsetPixels,\n              0, (int) (targetInfo.offset * size), newDuration);\n        }\n      } else {\n        final ItemInfo ii = infoForPosition(mCurItem);\n        final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;\n        final int scrollPos = (int) (scrollOffset *\n            (size - getPaddingTop() - getPaddingBottom()));\n        if (scrollPos != getScrollY()) {\n          completeScroll(false);\n          scrollTo(getScrollX(), scrollPos);\n        }\n      }\n    } else {\n      if (oldSize > 0 && !mItems.isEmpty()) {\n        final int widthWithMargin = size - getPaddingLeft() - getPaddingRight() + margin;\n        final int oldWidthWithMargin = oldSize - getPaddingLeft() - getPaddingRight()\n            + oldMargin;\n        final int xpos = getScrollX();\n        final float pageOffset = (float) xpos / oldWidthWithMargin;\n        final int newOffsetPixels = (int) (pageOffset * widthWithMargin);\n\n        scrollTo(newOffsetPixels, getScrollY());\n        if (!mScroller.isFinished()) {\n          // We now return to your regularly scheduled scroll, already in progress.\n          final int newDuration = mScroller.getDuration() - mScroller.timePassed();\n          ItemInfo targetInfo = infoForPosition(mCurItem);\n          mScroller.startScroll(newOffsetPixels, 0,\n              (int) (targetInfo.offset * size), 0, newDuration);\n        }\n      } else {\n        final ItemInfo ii = infoForPosition(mCurItem);\n        final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;\n        final int scrollPos = (int) (scrollOffset *\n            (size - getPaddingLeft() - getPaddingRight()));\n        if (scrollPos != getScrollX()) {\n          completeScroll(false);\n          scrollTo(scrollPos, getScrollY());\n        }\n      }\n    }\n  }\n\n  @Override\n  protected void onLayout(boolean changed, int l, int t, int r, int b) {\n    final int count = getChildCount();\n    int width = r - l;\n    int height = b - t;\n    int paddingLeft = getPaddingLeft();\n    int paddingTop = getPaddingTop();\n    int paddingRight = getPaddingRight();\n    int paddingBottom = getPaddingBottom();\n    final int scroll = (mOrientation == Orientation.VERTICAL) ? getScrollY() : getScrollX();\n\n    int decorCount = 0;\n\n    // First pass - decor views. We need to do this in two passes so that\n    // we have the proper offsets for non-decor views later.\n    for (int i = 0; i < count; i++) {\n      final View child = getChildAt(i);\n      if (child.getVisibility() != GONE) {\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        int childLeft = 0;\n        int childTop = 0;\n        if (lp.isDecor) {\n          final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;\n          final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;\n          switch (hgrav) {\n            default:\n              childLeft = paddingLeft;\n              break;\n            case Gravity.LEFT:\n              childLeft = paddingLeft;\n              paddingLeft += child.getMeasuredWidth();\n              break;\n            case Gravity.CENTER_HORIZONTAL:\n              childLeft = Math.max((width - child.getMeasuredWidth()) / 2,\n                  paddingLeft);\n              break;\n            case Gravity.RIGHT:\n              childLeft = width - paddingRight - child.getMeasuredWidth();\n              paddingRight += child.getMeasuredWidth();\n              break;\n          }\n          switch (vgrav) {\n            default:\n              childTop = paddingTop;\n              break;\n            case Gravity.TOP:\n              childTop = paddingTop;\n              paddingTop += child.getMeasuredHeight();\n              break;\n            case Gravity.CENTER_VERTICAL:\n              childTop = Math.max((height - child.getMeasuredHeight()) / 2,\n                  paddingTop);\n              break;\n            case Gravity.BOTTOM:\n              childTop = height - paddingBottom - child.getMeasuredHeight();\n              paddingBottom += child.getMeasuredHeight();\n              break;\n          }\n          if (mOrientation == Orientation.VERTICAL) {\n            childTop += scroll;\n          } else {\n            childLeft += scroll;\n          }\n          child.layout(childLeft, childTop,\n              childLeft + child.getMeasuredWidth(),\n              childTop + child.getMeasuredHeight());\n          decorCount++;\n        }\n      }\n    }\n\n    final int childSize =\n        (mOrientation == Orientation.VERTICAL) ? height - paddingTop - paddingBottom\n            : width - paddingLeft - paddingRight;\n    // Page views. Do this once we have the right padding offsets from above.\n    for (int i = 0; i < count; i++) {\n      final View child = getChildAt(i);\n      if (child.getVisibility() != GONE) {\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n        ItemInfo ii;\n        if (!lp.isDecor && (ii = infoForChild(child)) != null) {\n          int topLeftoff = (int) (childSize * ii.offset);\n          int childLeft;\n          int childTop;\n          if (mOrientation == Orientation.VERTICAL) {\n            childLeft = paddingLeft;\n            childTop = paddingTop + topLeftoff;\n            if (lp.needsMeasure) {\n              // This was added during layout and needs measurement.\n              // Do it now that we know what we're working with.\n              lp.needsMeasure = false;\n              final int widthSpec = MeasureSpec.makeMeasureSpec(\n                  (int) (width - paddingLeft - paddingRight),\n                  MeasureSpec.EXACTLY);\n              final int heightSpec = MeasureSpec.makeMeasureSpec(\n                  (int) (childSize * lp.heightFactor),\n                  MeasureSpec.EXACTLY);\n              child.measure(widthSpec, heightSpec);\n            }\n          } else {\n            childLeft = paddingLeft + topLeftoff;\n            childTop = paddingTop;\n            if (lp.needsMeasure) {\n              // This was added during layout and needs measurement.\n              // Do it now that we know what we're working with.\n              lp.needsMeasure = false;\n              final int widthSpec = MeasureSpec.makeMeasureSpec(\n                  (int) (childSize * lp.widthFactor),\n                  MeasureSpec.EXACTLY);\n              final int heightSpec = MeasureSpec.makeMeasureSpec(\n                  (int) (height - paddingTop - paddingBottom),\n                  MeasureSpec.EXACTLY);\n              child.measure(widthSpec, heightSpec);\n            }\n          }\n          if (DEBUG) {\n            Log.v(TAG, \"Positioning #\" + i + \" \" + child + \" f=\" + ii.object\n                + \":\" + childLeft + \",\" + childTop + \" \" + child.getMeasuredWidth()\n                + \"x\" + child.getMeasuredHeight());\n          }\n          child.layout(childLeft, childTop,\n              childLeft + child.getMeasuredWidth(),\n              childTop + child.getMeasuredHeight());\n        }\n      }\n    }\n    mTopLeftPageBounds = (mOrientation == Orientation.VERTICAL) ? paddingLeft : paddingTop;\n    mBottomRightPageBounds =\n        (mOrientation == Orientation.VERTICAL) ? width - paddingRight : height - paddingBottom;\n    mDecorChildCount = decorCount;\n\n    if (mFirstLayout) {\n      scrollToItem(mCurItem, false, 0, false);\n    }\n    mFirstLayout = false;\n  }\n\n  @Override\n  public void computeScroll() {\n    if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {\n      int oldX = getScrollX();\n      int oldY = getScrollY();\n      int x = mScroller.getCurrX();\n      int y = mScroller.getCurrY();\n\n      if (oldX != x || oldY != y) {\n        scrollTo(x, y);\n        if (mOrientation == Orientation.VERTICAL) {\n          if (!pageScrolled(y)) {\n            mScroller.abortAnimation();\n            scrollTo(x, 0);\n          }\n        } else {\n          if (!pageScrolled(x)) {\n            mScroller.abortAnimation();\n            scrollTo(0, y);\n          }\n        }\n      }\n\n      // Keep on drawing until the animation has finished.\n      ViewCompat.postInvalidateOnAnimation(this);\n      return;\n    }\n\n    // Done with scroll, clean up state.\n    completeScroll(true);\n  }\n\n  private boolean pageScrolled(int pos) {\n    if (mItems.size() == 0) {\n      mCalledSuper = false;\n      onPageScrolled(0, 0, 0);\n      if (!mCalledSuper) {\n        throw new IllegalStateException(\n            \"onPageScrolled did not call superclass implementation\");\n      }\n      return false;\n    }\n    final ItemInfo ii = infoForCurrentScrollPosition();\n    final int size = getClientSize();\n    final int sizeWithMargin = size + mPageMargin;\n    final float marginOffset = (float) mPageMargin / size;\n    final int currentPage = ii.position;\n    final float pageOffset = (((float) pos / size) - ii.offset) / (ii.sizeFactor + marginOffset);\n    final int offsetPixels = (int) (pageOffset * sizeWithMargin);\n\n    mCalledSuper = false;\n    onPageScrolled(currentPage, pageOffset, offsetPixels);\n    if (!mCalledSuper) {\n      throw new IllegalStateException(\n          \"onPageScrolled did not call superclass implementation\");\n    }\n    return true;\n  }\n\n  /**\n   * This method will be invoked when the current page is scrolled, either as part\n   * of a programmatically initiated smooth scroll or a user initiated touch scroll.\n   * If you override this method you must call through to the superclass implementation\n   * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled\n   * returns.\n   *\n   * @param position Position index of the first page currently being displayed.\n   * Page position+1 will be visible if positionOffset is nonzero.\n   * @param offset Value from [0, 1) indicating the offset from the page at position.\n   * @param offsetPixels Value in pixels indicating the offset from position.\n   */\n  protected void onPageScrolled(int position, float offset, int offsetPixels) {\n    // Offset any decor views if needed - keep them on-screen at all times.\n    if (mDecorChildCount > 0) {\n      if (mOrientation == Orientation.VERTICAL) {\n        final int scrollY = getScrollY();\n        int paddingTop = getPaddingTop();\n        int paddingBottom = getPaddingBottom();\n        final int height = getHeight();\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n          final View child = getChildAt(i);\n          final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n          if (!lp.isDecor) continue;\n\n          final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;\n          int childTop = 0;\n          switch (vgrav) {\n            default:\n              childTop = paddingTop;\n              break;\n            case Gravity.TOP:\n              childTop = paddingTop;\n              paddingTop += child.getHeight();\n              break;\n            case Gravity.CENTER_VERTICAL:\n              childTop = Math.max((height - child.getMeasuredHeight()) / 2,\n                  paddingTop);\n              break;\n            case Gravity.BOTTOM:\n              childTop = height - paddingBottom - child.getMeasuredHeight();\n              paddingBottom += child.getMeasuredHeight();\n              break;\n          }\n          childTop += scrollY;\n\n          final int childOffset = childTop - child.getTop();\n          if (childOffset != 0) {\n            child.offsetTopAndBottom(childOffset);\n          }\n        }\n      } else {\n        final int scrollX = getScrollX();\n        int paddingLeft = getPaddingLeft();\n        int paddingRight = getPaddingRight();\n        final int width = getWidth();\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n          final View child = getChildAt(i);\n          final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n          if (!lp.isDecor) continue;\n\n          final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;\n          int childLeft = 0;\n          switch (hgrav) {\n            default:\n              childLeft = paddingLeft;\n              break;\n            case Gravity.LEFT:\n              childLeft = paddingLeft;\n              paddingLeft += child.getWidth();\n              break;\n            case Gravity.CENTER_HORIZONTAL:\n              childLeft = Math.max((width - child.getMeasuredWidth()) / 2,\n                  paddingLeft);\n              break;\n            case Gravity.RIGHT:\n              childLeft = width - paddingRight - child.getMeasuredWidth();\n              paddingRight += child.getMeasuredWidth();\n              break;\n          }\n          childLeft += scrollX;\n\n          final int childOffset = childLeft - child.getLeft();\n          if (childOffset != 0) {\n            child.offsetLeftAndRight(childOffset);\n          }\n        }\n      }\n    }\n\n    if (mOnPageChangeListener != null) {\n      mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);\n    }\n    if (mInternalPageChangeListener != null) {\n      mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);\n    }\n\n    if (mPageTransformer != null) {\n      final int scroll = (mOrientation == Orientation.VERTICAL) ? getScrollY() : getScrollX();\n      final int childCount = getChildCount();\n      for (int i = 0; i < childCount; i++) {\n        final View child = getChildAt(i);\n        final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n\n        if (lp.isDecor) continue;\n\n        final float transformPos =\n            (float) (((mOrientation == Orientation.VERTICAL) ? child.getTop() : child.getLeft())\n                - scroll) / getClientSize();\n        mPageTransformer.transformPage(child, transformPos);\n      }\n    }\n\n    mCalledSuper = true;\n  }\n\n  private void completeScroll(boolean postEvents) {\n    boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;\n    if (needPopulate) {\n      // Done with scroll, no longer want to cache view drawing.\n      setScrollingCacheEnabled(false);\n      mScroller.abortAnimation();\n      int oldX = getScrollX();\n      int oldY = getScrollY();\n      int x = mScroller.getCurrX();\n      int y = mScroller.getCurrY();\n      if (oldX != x || oldY != y) {\n        scrollTo(x, y);\n      }\n    }\n    mPopulatePending = false;\n    for (int i = 0; i < mItems.size(); i++) {\n      ItemInfo ii = mItems.get(i);\n      if (ii.scrolling) {\n        needPopulate = true;\n        ii.scrolling = false;\n      }\n    }\n    if (needPopulate) {\n      if (postEvents) {\n        ViewCompat.postOnAnimation(this, mEndScrollRunnable);\n      } else {\n        mEndScrollRunnable.run();\n      }\n    }\n  }\n\n  private boolean isGutterDrag(float axis, float dAxis) {\n    return (axis < mGutterSize && dAxis > 0) || (axis\n        > (mOrientation == Orientation.VERTICAL ? getHeight() : getWidth()) - mGutterSize\n        && dAxis < 0);\n  }\n\n  private void enableLayers(boolean enable) {\n    final int childCount = getChildCount();\n    for (int i = 0; i < childCount; i++) {\n      final int layerType = enable ?\n          ViewCompat.LAYER_TYPE_HARDWARE : ViewCompat.LAYER_TYPE_NONE;\n      ViewCompat.setLayerType(getChildAt(i), layerType, null);\n    }\n  }\n\n  @Override\n  public boolean onInterceptTouchEvent(MotionEvent ev) {\n    /*\n     * This method JUST determines whether we want to intercept the motion.\n     * If we return true, onMotionEvent will be called and we do the actual\n     * scrolling there.\n     */\n\n    final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;\n\n    // Always take care of the touch gesture being complete.\n    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {\n      // Release the drag.\n      if (DEBUG) Log.v(TAG, \"Intercept done!\");\n      mIsBeingDragged = false;\n      mIsUnableToDrag = false;\n      mActivePointerId = INVALID_POINTER;\n      if (mVelocityTracker != null) {\n        mVelocityTracker.recycle();\n        mVelocityTracker = null;\n      }\n      return false;\n    }\n\n    // Nothing more to do here if we have decided whether or not we\n    // are dragging.\n    if (action != MotionEvent.ACTION_DOWN) {\n      if (mIsBeingDragged) {\n        if (DEBUG) Log.v(TAG, \"Intercept returning true!\");\n        return true;\n      }\n      if (mIsUnableToDrag) {\n        if (DEBUG) Log.v(TAG, \"Intercept returning false!\");\n        return false;\n      }\n    }\n\n    switch (action) {\n      case MotionEvent.ACTION_MOVE: {\n        /*\n         * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check\n         * whether the user has moved far enough from his original down touch.\n         */\n\n        /*\n         * Locally do absolute value. mLastMotionY is set to the y value\n         * of the down event.\n         */\n        final int activePointerId = mActivePointerId;\n        if (activePointerId == INVALID_POINTER) {\n          // If we don't have a valid id, the touch down wasn't on content.\n          break;\n        }\n\n        final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);\n        if (mOrientation == Orientation.VERTICAL) {\n          final float y = MotionEventCompat.getY(ev, pointerIndex);\n          final float dy = y - mLastMotionY;\n          final float yDiff = Math.abs(dy);\n          final float x = MotionEventCompat.getX(ev, pointerIndex);\n          final float xDiff = Math.abs(x - mInitialMotionX);\n          if (DEBUG) {\n            Log.v(TAG, \"Moved x to \" + x + \",\" + y + \" diff=\" + xDiff + \",\" + yDiff);\n          }\n\n          if (dy != 0 && !isGutterDrag(mLastMotionY, dy) &&\n              canScroll(this, false, (int) dy, (int) x, (int) y)) {\n            // Nested view has scrollable area under this point. Let it be handled there.\n            mLastMotionX = x;\n            mLastMotionY = y;\n            mIsUnableToDrag = true;\n            return false;\n          }\n          if (yDiff > mTouchSlop && yDiff * 0.5f > xDiff) {\n            if (DEBUG) Log.v(TAG, \"Starting drag!\");\n            mIsBeingDragged = true;\n            requestParentDisallowInterceptTouchEvent(true);\n            setScrollState(SCROLL_STATE_DRAGGING);\n            mLastMotionY = dy > 0 ? mInitialMotionY + mTouchSlop :\n                mInitialMotionY - mTouchSlop;\n            mLastMotionX = x;\n            setScrollingCacheEnabled(true);\n          } else if (xDiff > mTouchSlop) {\n            // The finger has moved enough in the vertical\n            // direction to be counted as a drag...  abort\n            // any attempt to drag horizontally, to work correctly\n            // with children that have scrolling containers.\n            if (DEBUG) Log.v(TAG, \"Starting unable to drag!\");\n            mIsUnableToDrag = true;\n          }\n          if (mIsBeingDragged) {\n            // Scroll to follow the motion event\n            if (performDrag(y)) {\n              ViewCompat.postInvalidateOnAnimation(this);\n            }\n          }\n        } else {\n          final float x = MotionEventCompat.getX(ev, pointerIndex);\n          final float dx = x - mLastMotionX;\n          final float xDiff = Math.abs(dx);\n          final float y = MotionEventCompat.getY(ev, pointerIndex);\n          final float yDiff = Math.abs(y - mInitialMotionY);\n          if (DEBUG) {\n            Log.v(TAG, \"Moved x to \" + x + \",\" + y + \" diff=\" + xDiff + \",\" + yDiff);\n          }\n\n          if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&\n              canScroll(this, false, (int) dx, (int) x, (int) y)) {\n            // Nested view has scrollable area under this point. Let it be handled there.\n            mLastMotionX = x;\n            mLastMotionY = y;\n            mIsUnableToDrag = true;\n            return false;\n          }\n          if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {\n            if (DEBUG) Log.v(TAG, \"Starting drag!\");\n            mIsBeingDragged = true;\n            requestParentDisallowInterceptTouchEvent(true);\n            setScrollState(SCROLL_STATE_DRAGGING);\n            mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :\n                mInitialMotionX - mTouchSlop;\n            mLastMotionY = y;\n            setScrollingCacheEnabled(true);\n          } else if (yDiff > mTouchSlop) {\n            // The finger has moved enough in the vertical\n            // direction to be counted as a drag...  abort\n            // any attempt to drag horizontally, to work correctly\n            // with children that have scrolling containers.\n            if (DEBUG) Log.v(TAG, \"Starting unable to drag!\");\n            mIsUnableToDrag = true;\n          }\n          if (mIsBeingDragged) {\n            // Scroll to follow the motion event\n            if (performDrag(x)) {\n              ViewCompat.postInvalidateOnAnimation(this);\n            }\n          }\n        }\n        break;\n      }\n\n      case MotionEvent.ACTION_DOWN: {\n        /*\n         * Remember location of down touch.\n         * ACTION_DOWN always refers to pointer index 0.\n         */\n        mLastMotionX = mInitialMotionX = ev.getX();\n        mLastMotionY = mInitialMotionY = ev.getY();\n        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\n        mIsUnableToDrag = false;\n\n        mScroller.computeScrollOffset();\n        if (mOrientation == Orientation.VERTICAL) {\n          if (mScrollState == SCROLL_STATE_SETTLING &&\n              Math.abs(mScroller.getFinalY() - mScroller.getCurrY()) > mCloseEnough) {\n            // Let the user 'catch' the pager as it animates.\n            mScroller.abortAnimation();\n            mPopulatePending = false;\n            populate();\n            mIsBeingDragged = true;\n            requestParentDisallowInterceptTouchEvent(true);\n            setScrollState(SCROLL_STATE_DRAGGING);\n          } else {\n            completeScroll(false);\n            mIsBeingDragged = false;\n          }\n        } else {\n          if (mScrollState == SCROLL_STATE_SETTLING &&\n              Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {\n            // Let the user 'catch' the pager as it animates.\n            mScroller.abortAnimation();\n            mPopulatePending = false;\n            populate();\n            mIsBeingDragged = true;\n            requestParentDisallowInterceptTouchEvent(true);\n            setScrollState(SCROLL_STATE_DRAGGING);\n          } else {\n            completeScroll(false);\n            mIsBeingDragged = false;\n          }\n        }\n\n        if (DEBUG) {\n          Log.v(TAG, \"Down at \" + mLastMotionX + \",\" + mLastMotionY\n              + \" mIsBeingDragged=\" + mIsBeingDragged\n              + \"mIsUnableToDrag=\" + mIsUnableToDrag);\n        }\n        break;\n      }\n\n      case MotionEventCompat.ACTION_POINTER_UP:\n        onSecondaryPointerUp(ev);\n        break;\n    }\n\n    if (mVelocityTracker == null) {\n      mVelocityTracker = VelocityTracker.obtain();\n    }\n    mVelocityTracker.addMovement(ev);\n\n    /*\n     * The only time we want to intercept motion events is if we are in the\n     * drag mode.\n     */\n    return mIsBeingDragged;\n  }\n\n  @Override\n  public boolean onTouchEvent(MotionEvent ev) {\n    if (mFakeDragging) {\n      // A fake drag is in progress already, ignore this real one\n      // but still eat the touch events.\n      // (It is likely that the user is multi-touching the screen.)\n      return true;\n    }\n\n    if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {\n      // Don't handle edge touches immediately -- they may actually belong to one of our\n      // descendants.\n      return false;\n    }\n\n    if (mAdapter == null || mAdapter.getCount() == 0) {\n      // Nothing to present or scroll; nothing to touch.\n      return false;\n    }\n\n    if (mVelocityTracker == null) {\n      mVelocityTracker = VelocityTracker.obtain();\n    }\n    mVelocityTracker.addMovement(ev);\n\n    final int action = ev.getAction();\n    boolean needsInvalidate = false;\n\n    switch (action & MotionEventCompat.ACTION_MASK) {\n      case MotionEvent.ACTION_DOWN: {\n        mScroller.abortAnimation();\n        mPopulatePending = false;\n        populate();\n\n        // Remember where the motion event started\n        mLastMotionX = mInitialMotionX = ev.getX();\n        mLastMotionY = mInitialMotionY = ev.getY();\n        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\n        break;\n      }\n      case MotionEvent.ACTION_MOVE:\n        if (!mIsBeingDragged) {\n          final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);\n          final float y = MotionEventCompat.getY(ev, pointerIndex);\n          final float yDiff = Math.abs(y - mLastMotionY);\n          final float x = MotionEventCompat.getX(ev, pointerIndex);\n          final float xDiff = Math.abs(x - mLastMotionX);\n          if (DEBUG) {\n            Log.v(TAG, \"Moved x to \" + x + \",\" + y + \" diff=\" + xDiff + \",\" + yDiff);\n          }\n          if (mOrientation == Orientation.VERTICAL) {\n            if (yDiff > mTouchSlop && yDiff > xDiff) {\n              if (DEBUG) Log.v(TAG, \"Starting drag!\");\n              mIsBeingDragged = true;\n              requestParentDisallowInterceptTouchEvent(true);\n              mLastMotionY = y - mInitialMotionY > 0 ? mInitialMotionY + mTouchSlop :\n                  mInitialMotionY - mTouchSlop;\n              mLastMotionX = x;\n              setScrollState(SCROLL_STATE_DRAGGING);\n              setScrollingCacheEnabled(true);\n\n              // Disallow Parent Intercept, just in case\n              ViewParent parent = getParent();\n              if (parent != null) {\n                parent.requestDisallowInterceptTouchEvent(true);\n              }\n            }\n          } else {\n            if (xDiff > mTouchSlop && xDiff > yDiff) {\n              if (DEBUG) Log.v(TAG, \"Starting drag!\");\n              mIsBeingDragged = true;\n              requestParentDisallowInterceptTouchEvent(true);\n              mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :\n                  mInitialMotionX - mTouchSlop;\n              mLastMotionY = y;\n              setScrollState(SCROLL_STATE_DRAGGING);\n              setScrollingCacheEnabled(true);\n\n              // Disallow Parent Intercept, just in case\n              ViewParent parent = getParent();\n              if (parent != null) {\n                parent.requestDisallowInterceptTouchEvent(true);\n              }\n            }\n          }\n        }\n        // Not else! Note that mIsBeingDragged can be set above.\n        if (mIsBeingDragged) {\n          // Scroll to follow the motion event\n          final int activePointerIndex = MotionEventCompat.findPointerIndex(\n              ev, mActivePointerId);\n          if (mOrientation == Orientation.VERTICAL) {\n            final float y = MotionEventCompat.getY(ev, activePointerIndex);\n            needsInvalidate |= performDrag(y);\n          } else {\n            final float x = MotionEventCompat.getX(ev, activePointerIndex);\n            needsInvalidate |= performDrag(x);\n          }\n        }\n        break;\n      case MotionEvent.ACTION_UP:\n        if (mIsBeingDragged) {\n          int currentPage;\n          int initialVelocity;\n          int totalDelta;\n          float pageOffset;\n          if (mOrientation == Orientation.VERTICAL) {\n            final VelocityTracker velocityTracker = mVelocityTracker;\n            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\n            initialVelocity = (int) VelocityTrackerCompat.getYVelocity(\n                velocityTracker, mActivePointerId);\n            mPopulatePending = true;\n            final int height = getClientSize();\n            final int scrollY = getScrollY();\n            final ItemInfo ii = infoForCurrentScrollPosition();\n            currentPage = ii.position;\n            pageOffset = (((float) scrollY / height) - ii.offset) / ii.sizeFactor;\n            final int activePointerIndex =\n                MotionEventCompat.findPointerIndex(ev, mActivePointerId);\n            final float y = MotionEventCompat.getY(ev, activePointerIndex);\n            totalDelta = (int) (y - mInitialMotionY);\n          } else {\n            final VelocityTracker velocityTracker = mVelocityTracker;\n            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\n            initialVelocity = (int) VelocityTrackerCompat.getXVelocity(\n                velocityTracker, mActivePointerId);\n            mPopulatePending = true;\n            final int width = getClientSize();\n            final int scrollX = getScrollX();\n            final ItemInfo ii = infoForCurrentScrollPosition();\n            currentPage = ii.position;\n            pageOffset = (((float) scrollX / width) - ii.offset) / ii.sizeFactor;\n            final int activePointerIndex =\n                MotionEventCompat.findPointerIndex(ev, mActivePointerId);\n            final float x = MotionEventCompat.getX(ev, activePointerIndex);\n            totalDelta = (int) (x - mInitialMotionX);\n          }\n          int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,\n              totalDelta);\n          setCurrentItemInternal(nextPage, true, true, initialVelocity);\n\n          mActivePointerId = INVALID_POINTER;\n          endDrag();\n          needsInvalidate = mTopLeftEdge.onRelease() | mRightBottomEdge.onRelease();\n        }\n        break;\n      case MotionEvent.ACTION_CANCEL:\n        if (mIsBeingDragged) {\n          scrollToItem(mCurItem, true, 0, false);\n          mActivePointerId = INVALID_POINTER;\n          endDrag();\n          needsInvalidate = mTopLeftEdge.onRelease() | mRightBottomEdge.onRelease();\n        }\n        break;\n      case MotionEventCompat.ACTION_POINTER_DOWN: {\n        int index;\n        if (mOrientation == Orientation.VERTICAL) {\n          index = MotionEventCompat.getActionIndex(ev);\n          final float y = MotionEventCompat.getY(ev, index);\n          mLastMotionY = y;\n        } else {\n          index = MotionEventCompat.getActionIndex(ev);\n          final float x = MotionEventCompat.getX(ev, index);\n          mLastMotionX = x;\n        }\n        mActivePointerId = MotionEventCompat.getPointerId(ev, index);\n        break;\n      }\n      case MotionEventCompat.ACTION_POINTER_UP:\n        onSecondaryPointerUp(ev);\n        if (mOrientation == Orientation.VERTICAL) {\n          mLastMotionY = MotionEventCompat.getY(ev,\n              MotionEventCompat.findPointerIndex(ev, mActivePointerId));\n        } else {\n          mLastMotionX = MotionEventCompat.getX(ev,\n              MotionEventCompat.findPointerIndex(ev, mActivePointerId));\n        }\n        break;\n    }\n    if (needsInvalidate) {\n      ViewCompat.postInvalidateOnAnimation(this);\n    }\n    return true;\n  }\n\n  private void requestParentDisallowInterceptTouchEvent(boolean disallowIntercept) {\n    final ViewParent parent = getParent();\n    if (parent != null) {\n      parent.requestDisallowInterceptTouchEvent(disallowIntercept);\n    }\n  }\n\n  private boolean performDrag(float dimen) {\n    boolean needsInvalidate = false;\n\n    if (mOrientation == Orientation.VERTICAL) {\n      float y = dimen;\n      final float deltaY = mLastMotionY - y;\n      mLastMotionY = y;\n\n      float oldScrollY = getScrollY();\n      float scrollY = oldScrollY + deltaY;\n      final int height = getClientSize();\n\n      float topBound = height * mFirstOffset;\n      float bottomBound = height * mLastOffset;\n      boolean topAbsolute = true;\n      boolean bottomAbsolute = true;\n\n      final ItemInfo firstItem = mItems.get(0);\n      final ItemInfo lastItem = mItems.get(mItems.size() - 1);\n      if (firstItem.position != 0) {\n        topAbsolute = false;\n        topBound = firstItem.offset * height;\n      }\n      if (lastItem.position != mAdapter.getCount() - 1) {\n        bottomAbsolute = false;\n        bottomBound = lastItem.offset * height;\n      }\n\n      if (scrollY < topBound) {\n        if (topAbsolute) {\n          float over = topBound - scrollY;\n          needsInvalidate = mTopLeftEdge.onPull(Math.abs(over) / height);\n        }\n        scrollY = topBound;\n      } else if (scrollY > bottomBound) {\n        if (bottomAbsolute) {\n          float over = scrollY - bottomBound;\n          needsInvalidate = mRightBottomEdge.onPull(Math.abs(over) / height);\n        }\n        scrollY = bottomBound;\n      }\n      // Don't lose the rounded component\n      mLastMotionX += scrollY - (int) scrollY;\n      scrollTo(getScrollX(), (int) scrollY);\n      pageScrolled((int) scrollY);\n    } else {\n      float x = dimen;\n\n      final float deltaX = mLastMotionX - x;\n      mLastMotionX = x;\n\n      float oldScrollX = getScrollX();\n      float scrollX = oldScrollX + deltaX;\n      final int width = getClientSize();\n\n      float leftBound = width * mFirstOffset;\n      float rightBound = width * mLastOffset;\n      boolean leftAbsolute = true;\n      boolean rightAbsolute = true;\n\n      final ItemInfo firstItem = mItems.get(0);\n      final ItemInfo lastItem = mItems.get(mItems.size() - 1);\n      if (firstItem.position != 0) {\n        leftAbsolute = false;\n        leftBound = firstItem.offset * width;\n      }\n      if (lastItem.position != mAdapter.getCount() - 1) {\n        rightAbsolute = false;\n        rightBound = lastItem.offset * width;\n      }\n\n      if (scrollX < leftBound) {\n        if (leftAbsolute) {\n          float over = leftBound - scrollX;\n          needsInvalidate = mTopLeftEdge.onPull(Math.abs(over) / width);\n        }\n        scrollX = leftBound;\n      } else if (scrollX > rightBound) {\n        if (rightAbsolute) {\n          float over = scrollX - rightBound;\n          needsInvalidate = mRightBottomEdge.onPull(Math.abs(over) / width);\n        }\n        scrollX = rightBound;\n      }\n      // Don't lose the rounded component\n      mLastMotionX += scrollX - (int) scrollX;\n      scrollTo((int) scrollX, getScrollY());\n      pageScrolled((int) scrollX);\n    }\n\n    return needsInvalidate;\n  }\n\n  /**\n   * @return Info about the page at the current scroll position.\n   * This can be synthetic for a missing middle page; the 'object' field can be null.\n   */\n  private ItemInfo infoForCurrentScrollPosition() {\n    final int size = getClientSize();\n    final float scrollOffset =\n        size > 0 ? (float) ((mOrientation == Orientation.VERTICAL) ? getScrollY() : getScrollX())\n            / size : 0;\n    final float marginOffset = size > 0 ? (float) mPageMargin / size : 0;\n    int lastPos = -1;\n    float lastOffset = 0.f;\n    float lastSize = 0.f;\n    boolean first = true;\n\n    ItemInfo lastItem = null;\n    for (int i = 0; i < mItems.size(); i++) {\n      ItemInfo ii = mItems.get(i);\n      float offset;\n      if (!first && ii.position != lastPos + 1) {\n        // Create a synthetic item for a missing page.\n        ii = mTempItem;\n        ii.offset = lastOffset + lastSize + marginOffset;\n        ii.position = lastPos + 1;\n        ii.sizeFactor = mAdapter.getPageWidth(ii.position);\n        i--;\n      }\n      offset = ii.offset;\n\n      final float topLeftBound = offset;\n      final float bottomRightBound = offset + ii.sizeFactor + marginOffset;\n      if (first || scrollOffset >= topLeftBound) {\n        if (scrollOffset < bottomRightBound || i == mItems.size() - 1) {\n          return ii;\n        }\n      } else {\n        return lastItem;\n      }\n      first = false;\n      lastPos = ii.position;\n      lastOffset = offset;\n      lastSize = ii.sizeFactor;\n      lastItem = ii;\n    }\n\n    return lastItem;\n  }\n\n  private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaDimen) {\n    int targetPage;\n    if (Math.abs(deltaDimen) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {\n      targetPage = velocity > 0 ? currentPage : currentPage + 1;\n    } else {\n      final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;\n      targetPage = (int) (currentPage + pageOffset + truncator);\n    }\n\n    if (mItems.size() > 0) {\n      final ItemInfo firstItem = mItems.get(0);\n      final ItemInfo lastItem = mItems.get(mItems.size() - 1);\n\n      // Only let the user target pages we have items for\n      targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));\n    }\n\n    return targetPage;\n  }\n\n  @Override\n  public void draw(Canvas canvas) {\n    super.draw(canvas);\n    boolean needsInvalidate = false;\n\n    final int overScrollMode = ViewCompat.getOverScrollMode(this);\n    if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||\n        (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&\n            mAdapter != null && mAdapter.getCount() > 1)) {\n      if (mOrientation == Orientation.VERTICAL) {\n        if (!mTopLeftEdge.isFinished()) {\n          final int restoreCount = canvas.save();\n          final int height = getHeight();\n          final int width = getWidth() - getPaddingLeft() - getPaddingRight();\n\n          canvas.translate(getPaddingLeft(), mFirstOffset * height);\n          mTopLeftEdge.setSize(width, height);\n          needsInvalidate |= mTopLeftEdge.draw(canvas);\n          canvas.restoreToCount(restoreCount);\n        }\n        if (!mRightBottomEdge.isFinished()) {\n          final int restoreCount = canvas.save();\n          final int height = getHeight();\n          final int width = getWidth() - getPaddingLeft() - getPaddingRight();\n\n          canvas.rotate(180);\n          canvas.translate(-width - getPaddingLeft(), -(mLastOffset + 1) * height);\n          mRightBottomEdge.setSize(width, height);\n          needsInvalidate |= mRightBottomEdge.draw(canvas);\n          canvas.restoreToCount(restoreCount);\n        }\n      } else {\n        if (!mTopLeftEdge.isFinished()) {\n          final int restoreCount = canvas.save();\n          final int height = getHeight() - getPaddingTop() - getPaddingBottom();\n          final int width = getWidth();\n\n          canvas.rotate(270);\n          canvas.translate(-height + getPaddingTop(), mFirstOffset * width);\n          mTopLeftEdge.setSize(height, width);\n          needsInvalidate |= mTopLeftEdge.draw(canvas);\n          canvas.restoreToCount(restoreCount);\n        }\n        if (!mRightBottomEdge.isFinished()) {\n          final int restoreCount = canvas.save();\n          final int width = getWidth();\n          final int height = getHeight() - getPaddingTop() - getPaddingBottom();\n\n          canvas.rotate(90);\n          canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * width);\n          mRightBottomEdge.setSize(height, width);\n          needsInvalidate |= mRightBottomEdge.draw(canvas);\n          canvas.restoreToCount(restoreCount);\n        }\n      }\n    } else {\n      mTopLeftEdge.finish();\n      mRightBottomEdge.finish();\n    }\n\n    if (needsInvalidate) {\n      // Keep animating\n      ViewCompat.postInvalidateOnAnimation(this);\n    }\n  }\n\n  @Override\n  protected void onDraw(Canvas canvas) {\n    super.onDraw(canvas);\n\n    // Draw the margin drawable between pages if needed.\n    if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {\n      if (mOrientation == Orientation.VERTICAL) {\n        final int scrollY = getScrollY();\n        final int height = getHeight();\n\n        final float marginOffset = (float) mPageMargin / height;\n        int itemIndex = 0;\n        ItemInfo ii = mItems.get(0);\n        float offset = ii.offset;\n        final int itemCount = mItems.size();\n        final int firstPos = ii.position;\n        final int lastPos = mItems.get(itemCount - 1).position;\n        for (int pos = firstPos; pos < lastPos; pos++) {\n          while (pos > ii.position && itemIndex < itemCount) {\n            ii = mItems.get(++itemIndex);\n          }\n\n          float drawAt;\n          if (pos == ii.position) {\n            drawAt = (ii.offset + ii.sizeFactor) * height;\n            offset = ii.offset + ii.sizeFactor + marginOffset;\n          } else {\n            float heightFactor = mAdapter.getPageWidth(pos);\n            drawAt = (offset + heightFactor) * height;\n            offset += heightFactor + marginOffset;\n          }\n\n          if (drawAt + mPageMargin > scrollY) {\n            mMarginDrawable.setBounds(mTopLeftPageBounds, (int) drawAt,\n                mBottomRightPageBounds, (int) (drawAt + mPageMargin + 0.5f));\n            mMarginDrawable.draw(canvas);\n          }\n\n          if (drawAt > scrollY + height) {\n            break; // No more visible, no sense in continuing\n          }\n        }\n      } else {\n        final int scrollX = getScrollX();\n        final int width = getWidth();\n\n        final float marginOffset = (float) mPageMargin / width;\n        int itemIndex = 0;\n        ItemInfo ii = mItems.get(0);\n        float offset = ii.offset;\n        final int itemCount = mItems.size();\n        final int firstPos = ii.position;\n        final int lastPos = mItems.get(itemCount - 1).position;\n        for (int pos = firstPos; pos < lastPos; pos++) {\n          while (pos > ii.position && itemIndex < itemCount) {\n            ii = mItems.get(++itemIndex);\n          }\n\n          float drawAt;\n          if (pos == ii.position) {\n            drawAt = (ii.offset + ii.sizeFactor) * width;\n            offset = ii.offset + ii.sizeFactor + marginOffset;\n          } else {\n            float widthFactor = mAdapter.getPageWidth(pos);\n            drawAt = (offset + widthFactor) * width;\n            offset += widthFactor + marginOffset;\n          }\n\n          if (drawAt + mPageMargin > scrollX) {\n            mMarginDrawable.setBounds((int) drawAt, mTopLeftPageBounds,\n                (int) (drawAt + mPageMargin + 0.5f), mBottomRightPageBounds);\n            mMarginDrawable.draw(canvas);\n          }\n\n          if (drawAt > scrollX + width) {\n            break; // No more visible, no sense in continuing\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Start a fake drag of the pager.\n   * <p/>\n   * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager\n   * with the touch scrolling of another view, while still letting the ViewPager\n   * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)\n   * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call\n   * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.\n   * <p/>\n   * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag\n   * is already in progress, this method will return false.\n   *\n   * @return true if the fake drag began successfully, false if it could not be started.\n   * @see #fakeDragBy(float)\n   * @see #endFakeDrag()\n   */\n  public boolean beginFakeDrag() {\n    if (mIsBeingDragged) {\n      return false;\n    }\n    mFakeDragging = true;\n    setScrollState(SCROLL_STATE_DRAGGING);\n    if (mOrientation == Orientation.VERTICAL) {\n      mInitialMotionY = mLastMotionY = 0;\n    } else {\n      mInitialMotionX = mLastMotionX = 0;\n    }\n    if (mVelocityTracker == null) {\n      mVelocityTracker = VelocityTracker.obtain();\n    } else {\n      mVelocityTracker.clear();\n    }\n    final long time = SystemClock.uptimeMillis();\n    final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);\n    mVelocityTracker.addMovement(ev);\n    ev.recycle();\n    mFakeDragBeginTime = time;\n    return true;\n  }\n\n  /**\n   * End a fake drag of the pager.\n   *\n   * @see #beginFakeDrag()\n   * @see #fakeDragBy(float)\n   */\n  public void endFakeDrag() {\n    if (!mFakeDragging) {\n      throw new IllegalStateException(\"No fake drag in progress. Call beginFakeDrag first.\");\n    }\n\n    final VelocityTracker velocityTracker = mVelocityTracker;\n    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);\n    if (mOrientation == Orientation.VERTICAL) {\n      int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(\n          velocityTracker, mActivePointerId);\n      mPopulatePending = true;\n      final int size = getClientSize();\n      final int scrollY = getScrollY();\n      final ItemInfo ii = infoForCurrentScrollPosition();\n      final int currentPage = ii.position;\n      final float pageOffset = (((float) scrollY / size) - ii.offset) / ii.sizeFactor;\n      final int totalDelta = (int) (mLastMotionY - mInitialMotionY);\n\n      int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,\n          totalDelta);\n      setCurrentItemInternal(nextPage, true, true, initialVelocity);\n    } else {\n      int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(\n          velocityTracker, mActivePointerId);\n      mPopulatePending = true;\n      final int size = getClientSize();\n      final int scrollX = getScrollX();\n      final ItemInfo ii = infoForCurrentScrollPosition();\n      final int currentPage = ii.position;\n      final float pageOffset = (((float) scrollX / size) - ii.offset) / ii.sizeFactor;\n      final int totalDelta = (int) (mLastMotionX - mInitialMotionX);\n      int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,\n          totalDelta);\n      setCurrentItemInternal(nextPage, true, true, initialVelocity);\n    }\n    endDrag();\n\n    mFakeDragging = false;\n  }\n\n  /**\n   * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.\n   *\n   * @param offset Offset in pixels to drag by.\n   * @see #beginFakeDrag()\n   * @see #endFakeDrag()\n   */\n  public void fakeDragBy(float offset) {\n    if (!mFakeDragging) {\n      throw new IllegalStateException(\"No fake drag in progress. Call beginFakeDrag first.\");\n    }\n\n    if (mOrientation == Orientation.VERTICAL) {\n      mLastMotionY += offset;\n\n      float oldScrollY = getScrollY();\n      float scrollY = oldScrollY - offset;\n      final int height = getClientSize();\n\n      float topBound = height * mFirstOffset;\n      float bottomBound = height * mLastOffset;\n\n      final ItemInfo firstItem = mItems.get(0);\n      final ItemInfo lastItem = mItems.get(mItems.size() - 1);\n      if (firstItem.position != 0) {\n        topBound = firstItem.offset * height;\n      }\n      if (lastItem.position != mAdapter.getCount() - 1) {\n        bottomBound = lastItem.offset * height;\n      }\n\n      if (scrollY < topBound) {\n        scrollY = topBound;\n      } else if (scrollY > bottomBound) {\n        scrollY = bottomBound;\n      }\n      // Don't lose the rounded component\n      mLastMotionY += scrollY - (int) scrollY;\n      scrollTo(getScrollX(), (int) scrollY);\n      pageScrolled((int) scrollY);\n\n      // Synthesize an event for the VelocityTracker.\n      final long time = SystemClock.uptimeMillis();\n      final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,\n          0, mLastMotionY, 0);\n      mVelocityTracker.addMovement(ev);\n      ev.recycle();\n    } else {\n      mLastMotionX += offset;\n\n      float oldScrollX = getScrollX();\n      float scrollX = oldScrollX - offset;\n      final int width = getClientSize();\n\n      float leftBound = width * mFirstOffset;\n      float rightBound = width * mLastOffset;\n\n      final ItemInfo firstItem = mItems.get(0);\n      final ItemInfo lastItem = mItems.get(mItems.size() - 1);\n      if (firstItem.position != 0) {\n        leftBound = firstItem.offset * width;\n      }\n      if (lastItem.position != mAdapter.getCount() - 1) {\n        rightBound = lastItem.offset * width;\n      }\n\n      if (scrollX < leftBound) {\n        scrollX = leftBound;\n      } else if (scrollX > rightBound) {\n        scrollX = rightBound;\n      }\n      // Don't lose the rounded component\n      mLastMotionX += scrollX - (int) scrollX;\n      scrollTo((int) scrollX, getScrollY());\n      pageScrolled((int) scrollX);\n\n      // Synthesize an event for the VelocityTracker.\n      final long time = SystemClock.uptimeMillis();\n      final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,\n          mLastMotionX, 0, 0);\n      mVelocityTracker.addMovement(ev);\n      ev.recycle();\n    }\n  }\n\n  /**\n   * Returns true if a fake drag is in progress.\n   *\n   * @return true if currently in a fake drag, false otherwise.\n   * @see #beginFakeDrag()\n   * @see #fakeDragBy(float)\n   * @see #endFakeDrag()\n   */\n  public boolean isFakeDragging() {\n    return mFakeDragging;\n  }\n\n  private void onSecondaryPointerUp(MotionEvent ev) {\n    final int pointerIndex = MotionEventCompat.getActionIndex(ev);\n    final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);\n    if (pointerId == mActivePointerId) {\n      // This was our active pointer going up. Choose a new\n      // active pointer and adjust accordingly.\n      final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n      if (mOrientation == Orientation.VERTICAL) {\n        mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex);\n      } else {\n        mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);\n      }\n      mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);\n      if (mVelocityTracker != null) {\n        mVelocityTracker.clear();\n      }\n    }\n  }\n\n  private void endDrag() {\n    mIsBeingDragged = false;\n    mIsUnableToDrag = false;\n\n    if (mVelocityTracker != null) {\n      mVelocityTracker.recycle();\n      mVelocityTracker = null;\n    }\n  }\n\n  private void setScrollingCacheEnabled(boolean enabled) {\n    if (mScrollingCacheEnabled != enabled) {\n      mScrollingCacheEnabled = enabled;\n      if (USE_CACHE) {\n        final int size = getChildCount();\n        for (int i = 0; i < size; ++i) {\n          final View child = getChildAt(i);\n          if (child.getVisibility() != GONE) {\n            child.setDrawingCacheEnabled(enabled);\n          }\n        }\n      }\n    }\n  }\n\n  public boolean internalCanScrollVertically(int direction) {\n    if (mAdapter == null) {\n      return false;\n    }\n\n    final int size = getClientSize();\n    final int scroll = (mOrientation == Orientation.VERTICAL) ? getScrollY() : getScrollX();\n    if (direction < 0) {\n      return (scroll > (int) (size * mFirstOffset));\n    } else if (direction > 0) {\n      return (scroll < (int) (size * mLastOffset));\n    } else {\n      return false;\n    }\n  }\n\n  /**\n   * Tests scrollability within child views of v given a delta of dx.\n   *\n   * @param v View to test for horizontal scrollability\n   * @param checkV Whether the view v passed should itself be checked for scrollability (true),\n   * or just its children (false).\n   * @param delta Delta scrolled in pixels\n   * @param x X coordinate of the active touch point\n   * @param y Y coordinate of the active touch point\n   * @return true if child views of v can be scrolled by delta of dx.\n   */\n  protected boolean canScroll(View v, boolean checkV, int delta, int x, int y) {\n    if (v instanceof ViewGroup) {\n      final ViewGroup group = (ViewGroup) v;\n      final int scrollX = v.getScrollX();\n      final int scrollY = v.getScrollY();\n      final int count = group.getChildCount();\n      // Count backwards - let topmost views consume scroll distance first.\n      for (int i = count - 1; i >= 0; i--) {\n        // TODO: Add versioned support here for transformed views.\n        // This will not work for transformed views in Honeycomb+\n        final View child = group.getChildAt(i);\n        if (mOrientation == Orientation.VERTICAL) {\n          if (y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&\n              x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&\n              canScroll(child, true, delta, x + scrollX - child.getLeft(),\n                  y + scrollY - child.getTop())) {\n            return true;\n          }\n        } else {\n          if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&\n              y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&\n              canScroll(child, true, delta, x + scrollX - child.getLeft(),\n                  y + scrollY - child.getTop())) {\n            return true;\n          }\n        }\n      }\n    }\n\n    return checkV && ViewCompat.canScrollVertically(v, -delta);\n  }\n\n  @Override\n  public boolean dispatchKeyEvent(KeyEvent event) {\n    // Let the focused view and/or our descendants get the key first\n    return super.dispatchKeyEvent(event) || executeKeyEvent(event);\n  }\n\n  /**\n   * You can call this function yourself to have the scroll view perform\n   * scrolling from a key event, just as if the event had been dispatched to\n   * it by the view hierarchy.\n   *\n   * @param event The key event to execute.\n   * @return Return true if the event was handled, else false.\n   */\n  public boolean executeKeyEvent(KeyEvent event) {\n    boolean handled = false;\n    if (event.getAction() == KeyEvent.ACTION_DOWN) {\n      switch (event.getKeyCode()) {\n        case KeyEvent.KEYCODE_DPAD_LEFT:\n          handled = arrowScroll(FOCUS_LEFT);\n          break;\n        case KeyEvent.KEYCODE_DPAD_RIGHT:\n          handled = arrowScroll(FOCUS_RIGHT);\n          break;\n        case KeyEvent.KEYCODE_TAB:\n          if (Build.VERSION.SDK_INT >= 11) {\n            // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD\n            // before Android 3.0. Ignore the tab key on those devices.\n            if (event.hasNoModifiers()) {\n              handled = arrowScroll(FOCUS_FORWARD);\n            } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {\n              handled = arrowScroll(FOCUS_BACKWARD);\n            }\n          }\n          break;\n      }\n    }\n    return handled;\n  }\n\n  public boolean arrowScroll(int direction) {\n    View currentFocused = findFocus();\n    if (currentFocused == this) {\n      currentFocused = null;\n    } else if (currentFocused != null) {\n      boolean isChild = false;\n      for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;\n          parent = parent.getParent()) {\n        if (parent == this) {\n          isChild = true;\n          break;\n        }\n      }\n      if (!isChild) {\n        // This would cause the focus search down below to fail in fun ways.\n        final StringBuilder sb = new StringBuilder();\n        sb.append(currentFocused.getClass().getSimpleName());\n        for (ViewParent parent = currentFocused.getParent(); parent instanceof ViewGroup;\n            parent = parent.getParent()) {\n          sb.append(\" => \").append(parent.getClass().getSimpleName());\n        }\n        Log.e(TAG, \"arrowScroll tried to find focus based on non-child \" +\n            \"current focused view \" + sb.toString());\n        currentFocused = null;\n      }\n    }\n\n    boolean handled = false;\n\n    View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,\n        direction);\n    if (nextFocused != null && nextFocused != currentFocused) {\n      if (direction == View.FOCUS_UP) {\n        // If there is nothing to the left, or this is causing us to\n        // jump to the right, then what we really want to do is page left.\n        if (mOrientation == Orientation.VERTICAL) {\n          final int nextTop = getChildRectInPagerCoordinates(mTempRect, nextFocused).top;\n          final int currTop = getChildRectInPagerCoordinates(mTempRect, currentFocused).top;\n          if (currentFocused != null && nextTop >= currTop) {\n            handled = pageBack();\n          } else {\n            handled = nextFocused.requestFocus();\n          }\n        } else {\n          final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;\n          final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;\n          if (currentFocused != null && nextLeft >= currLeft) {\n            handled = pageBack();\n          } else {\n            handled = nextFocused.requestFocus();\n          }\n        }\n      } else if (direction == View.FOCUS_DOWN) {\n        // If there is nothing to the right, or this is causing us to\n        // jump to the left, then what we really want to do is page right.\n        if (mOrientation == Orientation.VERTICAL) {\n          final int nextDown = getChildRectInPagerCoordinates(mTempRect, nextFocused).bottom;\n          final int currDown = getChildRectInPagerCoordinates(mTempRect, currentFocused).bottom;\n          if (currentFocused != null && nextDown <= currDown) {\n            handled = pageForward();\n          } else {\n            handled = nextFocused.requestFocus();\n          }\n        } else {\n          final int nextLeft = getChildRectInPagerCoordinates(mTempRect, nextFocused).left;\n          final int currLeft = getChildRectInPagerCoordinates(mTempRect, currentFocused).left;\n          if (currentFocused != null && nextLeft <= currLeft) {\n            handled = pageForward();\n          } else {\n            handled = nextFocused.requestFocus();\n          }\n        }\n      }\n    } else if (direction == FOCUS_UP || direction == FOCUS_BACKWARD) {\n      // Trying to move left and nothing there; try to page.\n      handled = pageBack();\n    } else if (direction == FOCUS_DOWN || direction == FOCUS_FORWARD) {\n      // Trying to move right and nothing there; try to page.\n      handled = pageForward();\n    }\n    if (handled) {\n      playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));\n    }\n    return handled;\n  }\n\n  private Rect getChildRectInPagerCoordinates(Rect outRect, View child) {\n    if (outRect == null) {\n      outRect = new Rect();\n    }\n    if (child == null) {\n      outRect.set(0, 0, 0, 0);\n      return outRect;\n    }\n    outRect.left = child.getLeft();\n    outRect.right = child.getRight();\n    outRect.top = child.getTop();\n    outRect.bottom = child.getBottom();\n\n    ViewParent parent = child.getParent();\n    while (parent instanceof ViewGroup && parent != this) {\n      final ViewGroup group = (ViewGroup) parent;\n      outRect.left += group.getLeft();\n      outRect.right += group.getRight();\n      outRect.top += group.getTop();\n      outRect.bottom += group.getBottom();\n\n      parent = group.getParent();\n    }\n    return outRect;\n  }\n\n  boolean pageBack() {\n    if (mCurItem > 0) {\n      setCurrentItem(mCurItem - 1, true);\n      return true;\n    }\n    return false;\n  }\n\n  boolean pageForward() {\n    if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {\n      setCurrentItem(mCurItem + 1, true);\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * We only want the current page that is being shown to be focusable.\n   */\n  @Override\n  public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {\n    final int focusableCount = views.size();\n\n    final int descendantFocusability = getDescendantFocusability();\n\n    if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {\n      for (int i = 0; i < getChildCount(); i++) {\n        final View child = getChildAt(i);\n        if (child.getVisibility() == VISIBLE) {\n          ItemInfo ii = infoForChild(child);\n          if (ii != null && ii.position == mCurItem) {\n            child.addFocusables(views, direction, focusableMode);\n          }\n        }\n      }\n    }\n\n    // we add ourselves (if focusable) in all cases except for when we are\n    // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is\n    // to avoid the focus search finding layouts when a more precise search\n    // among the focusable children would be more interesting.\n    if (\n        descendantFocusability != FOCUS_AFTER_DESCENDANTS ||\n            // No focusable descendants\n            (focusableCount == views.size())) {\n      // Note that we can't call the superclass here, because it will\n      // add all views in.  So we need to do the same thing View does.\n      if (!isFocusable()) {\n        return;\n      }\n      if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&\n          isInTouchMode() && !isFocusableInTouchMode()) {\n        return;\n      }\n      if (views != null) {\n        views.add(this);\n      }\n    }\n  }\n\n  /**\n   * We only want the current page that is being shown to be touchable.\n   */\n  @Override\n  public void addTouchables(ArrayList<View> views) {\n    // Note that we don't call super.addTouchables(), which means that\n    // we don't call View.addTouchables().  This is okay because a ViewPager\n    // is itself not touchable.\n    for (int i = 0; i < getChildCount(); i++) {\n      final View child = getChildAt(i);\n      if (child.getVisibility() == VISIBLE) {\n        ItemInfo ii = infoForChild(child);\n        if (ii != null && ii.position == mCurItem) {\n          child.addTouchables(views);\n        }\n      }\n    }\n  }\n\n  /**\n   * We only want the current page that is being shown to be focusable.\n   */\n  @Override\n  protected boolean onRequestFocusInDescendants(int direction,\n      Rect previouslyFocusedRect) {\n    int index;\n    int increment;\n    int end;\n    int count = getChildCount();\n    if ((direction & FOCUS_FORWARD) != 0) {\n      index = 0;\n      increment = 1;\n      end = count;\n    } else {\n      index = count - 1;\n      increment = -1;\n      end = -1;\n    }\n    for (int i = index; i != end; i += increment) {\n      View child = getChildAt(i);\n      if (child.getVisibility() == VISIBLE) {\n        ItemInfo ii = infoForChild(child);\n        if (ii != null && ii.position == mCurItem) {\n          if (child.requestFocus(direction, previouslyFocusedRect)) {\n            return true;\n          }\n        }\n      }\n    }\n    return false;\n  }\n\n  @Override\n  public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {\n    // Dispatch scroll events from this ViewPager.\n    if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED) {\n      return super.dispatchPopulateAccessibilityEvent(event);\n    }\n\n    // Dispatch all other accessibility events from the current page.\n    final int childCount = getChildCount();\n    for (int i = 0; i < childCount; i++) {\n      final View child = getChildAt(i);\n      if (child.getVisibility() == VISIBLE) {\n        final ItemInfo ii = infoForChild(child);\n        if (ii != null && ii.position == mCurItem &&\n            child.dispatchPopulateAccessibilityEvent(event)) {\n          return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  @Override\n  protected ViewGroup.LayoutParams generateDefaultLayoutParams() {\n    return new LayoutParams();\n  }\n\n  @Override\n  protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {\n    return generateDefaultLayoutParams();\n  }\n\n  @Override\n  protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\n    return p instanceof LayoutParams && super.checkLayoutParams(p);\n  }\n\n  @Override\n  public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {\n    return new LayoutParams(getContext(), attrs);\n  }\n\n  class MyAccessibilityDelegate extends AccessibilityDelegateCompat {\n\n    @Override\n    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {\n      super.onInitializeAccessibilityEvent(host, event);\n      event.setClassName(ViewPager.class.getName());\n      final AccessibilityRecordCompat recordCompat = AccessibilityRecordCompat.obtain();\n      recordCompat.setScrollable(canScroll());\n      if (event.getEventType() == AccessibilityEventCompat.TYPE_VIEW_SCROLLED\n          && mAdapter != null) {\n        recordCompat.setItemCount(mAdapter.getCount());\n        recordCompat.setFromIndex(mCurItem);\n        recordCompat.setToIndex(mCurItem);\n      }\n    }\n\n    @Override\n    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {\n      super.onInitializeAccessibilityNodeInfo(host, info);\n      info.setClassName(ViewPager.class.getName());\n      info.setScrollable(canScroll());\n      if (mOrientation == Orientation.VERTICAL) {\n        if (internalCanScrollVertically(1)) {\n          info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);\n        }\n        if (internalCanScrollVertically(-1)) {\n          info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);\n        }\n      } else {\n        if (canScrollHorizontally(1)) {\n          info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);\n        }\n        if (canScrollHorizontally(-1)) {\n          info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);\n        }\n      }\n    }\n\n    @Override\n    public boolean performAccessibilityAction(View host, int action, Bundle args) {\n      if (super.performAccessibilityAction(host, action, args)) {\n        return true;\n      }\n      switch (action) {\n        case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {\n          if ((mOrientation == Orientation.VERTICAL && internalCanScrollVertically(1))\n              || (mOrientation == Orientation.HORIZONTAL && canScrollHorizontally(1))) {\n            setCurrentItem(mCurItem + 1);\n            return true;\n          }\n        }\n        return false;\n        case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {\n          if ((mOrientation == Orientation.VERTICAL && internalCanScrollVertically(-1))\n              || (mOrientation == Orientation.HORIZONTAL && canScrollHorizontally(-1))) {\n            setCurrentItem(mCurItem - 1);\n            return true;\n          }\n        }\n        return false;\n      }\n      return false;\n    }\n\n    private boolean canScroll() {\n      return (mAdapter != null) && (mAdapter.getCount() > 1);\n    }\n  }\n\n  private class PagerObserver extends DataSetObserver {\n    @Override\n    public void onChanged() {\n      dataSetChanged();\n    }\n\n    @Override\n    public void onInvalidated() {\n      dataSetChanged();\n    }\n  }\n\n  /**\n   * Layout parameters that should be supplied for views added to a\n   * ViewPager.\n   */\n  public static class LayoutParams extends ViewGroup.LayoutParams {\n    /**\n     * true if this view is a decoration on the pager itself and not\n     * a view supplied by the adapter.\n     */\n    public boolean isDecor;\n\n    /**\n     * Gravity setting for use on decor views only:\n     * Where to position the view page within the overall ViewPager\n     * container; constants are defined in {@link android.view.Gravity}.\n     */\n    public int gravity;\n\n    /**\n     * Width as a 0-1 multiplier of the measured pager height\n     */\n    float heightFactor = 0.f;\n\n    /**\n     * Width as a 0-1 multiplier of the measured pager width\n     */\n    float widthFactor = 0.f;\n\n    /**\n     * true if this view was added during layout and needs to be measured\n     * before being positioned.\n     */\n    boolean needsMeasure;\n\n    /**\n     * Adapter position this view is for if !isDecor\n     */\n    int position;\n\n    /**\n     * Current child index within the ViewPager that this view occupies\n     */\n    int childIndex;\n\n    public LayoutParams() {\n      super(FILL_PARENT, FILL_PARENT);\n    }\n\n    public LayoutParams(Context context, AttributeSet attrs) {\n      super(context, attrs);\n\n      final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);\n      gravity = a.getInteger(0, Gravity.TOP);\n      a.recycle();\n    }\n  }\n\n  static class ViewPositionComparator implements Comparator<View> {\n    @Override\n    public int compare(View lhs, View rhs) {\n      final LayoutParams llp = (LayoutParams) lhs.getLayoutParams();\n      final LayoutParams rlp = (LayoutParams) rhs.getLayoutParams();\n      if (llp.isDecor != rlp.isDecor) {\n        return llp.isDecor ? 1 : -1;\n      }\n      return llp.position - rlp.position;\n    }\n  }\n\n  // Following classes and the interface are needed for the Maven Central upload script to work properly.\n  // They are being introduced here, sort of temporarily (until I find a better solution for this issue).\n\n  /**\n   * Callbacks a {@link Parcelable} creator should implement.\n   */\n  public interface ParcelableCompatCreatorCallbacks<T> {\n\n    /**\n     * Create a new instance of the Parcelable class, instantiating it\n     * from the given Parcel whose data had previously been written by\n     * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and\n     * using the given ClassLoader.\n     *\n     * @param in The Parcel to read the object's data from.\n     * @param loader The ClassLoader that this object is being created in.\n     * @return Returns a new instance of the Parcelable class.\n     */\n    public T createFromParcel(Parcel in, ClassLoader loader);\n\n    /**\n     * Create a new array of the Parcelable class.\n     *\n     * @param size Size of the array.\n     * @return Returns an array of the Parcelable class, with every entry\n     * initialized to null.\n     */\n    public T[] newArray(int size);\n  }\n\n  /**\n   * Helper for accessing features in {@link android.os.Parcelable}\n   * introduced after API level 4 in a backwards compatible fashion.\n   */\n  public static class ParcelableCompat {\n\n    /**\n     * Factory method for {@link Parcelable.Creator}.\n     *\n     * @param callbacks Creator callbacks implementation.\n     * @return New creator.\n     */\n    public static <T> Parcelable.Creator<T> newCreator(\n        OrientedViewPager.ParcelableCompatCreatorCallbacks<T> callbacks) {\n      if (android.os.Build.VERSION.SDK_INT >= 13) {\n        return ParcelableCompatCreatorHoneycombMR2Stub.instantiate(callbacks);\n      }\n      return new CompatCreator<T>(callbacks);\n    }\n\n    public static class CompatCreator<T> implements Parcelable.Creator<T> {\n      final OrientedViewPager.ParcelableCompatCreatorCallbacks<T> mCallbacks;\n\n      public CompatCreator(OrientedViewPager.ParcelableCompatCreatorCallbacks<T> callbacks) {\n        mCallbacks = callbacks;\n      }\n\n      @Override\n      public T createFromParcel(Parcel source) {\n        return mCallbacks.createFromParcel(source, null);\n      }\n\n      @Override\n      public T[] newArray(int size) {\n        return mCallbacks.newArray(size);\n      }\n    }\n  }\n\n  static class ParcelableCompatCreatorHoneycombMR2Stub {\n    public static <T> Parcelable.Creator<T> instantiate(\n        OrientedViewPager.ParcelableCompatCreatorCallbacks<T> callbacks) {\n      return new ParcelableCompatCreatorHoneycombMR2<T>(callbacks);\n    }\n  }\n\n  static class ParcelableCompatCreatorHoneycombMR2<T> implements Parcelable.ClassLoaderCreator<T> {\n    private final OrientedViewPager.ParcelableCompatCreatorCallbacks<T> mCallbacks;\n\n    public ParcelableCompatCreatorHoneycombMR2(\n        OrientedViewPager.ParcelableCompatCreatorCallbacks<T> callbacks) {\n      mCallbacks = callbacks;\n    }\n\n    public T createFromParcel(Parcel in) {\n      return mCallbacks.createFromParcel(in, null);\n    }\n\n    public T createFromParcel(Parcel in, ClassLoader loader) {\n      return mCallbacks.createFromParcel(in, loader);\n    }\n\n    public T[] newArray(int size) {\n      return mCallbacks.newArray(size);\n    }\n  }\n}"
  },
  {
    "path": "library/src/main/java/com/gu/library/transformer/VerticalBaseTransformer.java",
    "content": "package com.gu.library.transformer;\n\nimport android.support.v4.view.ViewPager;\nimport android.view.View;\n\n/**\n * Created by Nate on 2016/7/22.\n */\npublic abstract class VerticalBaseTransformer implements ViewPager.PageTransformer {\n    /**\n     * Called each {@link #transformPage(View, float)}.\n     *\n     * @param page     Apply the transformation to this page\n     * @param position Position of page relative to the current front-and-center position of the pager. 0 is front and\n     *                 center. 1 is one full page position to the right, and -1 is one page position to the left.\n     */\n    protected abstract void onTransform(View page, float position);\n\n    /**\n     * Apply a property transformation to the given page. For most use cases, this method should not be overridden.\n     * Instead use {@link #transformPage(View, float)} to perform typical transformations.\n     *\n     * @param page     Apply the transformation to this page\n     * @param position Position of page relative to the current front-and-center position of the pager. 0 is front and\n     *                 center. 1 is one full page position to the right, and -1 is one page position to the left.\n     */\n    @Override\n    public void transformPage(View page, float position) {\n        onPreTransform(page, position);\n        onTransform(page, position);\n        onPostTransform(page, position);\n    }\n\n    /**\n     * If the position offset of a fragment is less than negative one or greater than one, returning true will set the\n     * fragment alpha to 0f. Otherwise fragment alpha is always defaulted to 1f.\n     *\n     * @return\n     */\n    protected boolean hideOffscreenPages() {\n        return true;\n    }\n\n    /**\n     * Indicates if the default animations of the view pager should be used.\n     *\n     * @return\n     */\n    protected boolean isPagingEnabled() {\n        return false;\n    }\n\n    /**\n     * Called each {@link #transformPage(View, float)} before {{@link #onTransform(View, float)}.\n     * <p/>\n     * The default implementation attempts to reset all view properties. This is useful when toggling transforms that do\n     * not modify the same page properties. For instance changing from a transformation that applies rotation to a\n     * transformation that fades can inadvertently leave a fragment stuck with a rotation or with some degree of applied\n     * alpha.\n     *\n     * @param page     Apply the transformation to this page\n     * @param position Position of page relative to the current front-and-center position of the pager. 0 is front and\n     *                 center. 1 is one full page position to the right, and -1 is one page position to the left.\n     */\n    protected void onPreTransform(View page, float position) {\n        final float width = page.getWidth();\n        final float height = page.getHeight();\n\n        page.setRotationX(0);\n        page.setRotationY(0);\n        page.setRotation(0);\n        page.setScaleX(1);\n        page.setScaleY(1);\n        page.setPivotX(0);\n        page.setPivotY(0);\n        page.setTranslationX(0);\n        page.setTranslationY(isPagingEnabled() ? 0f : -height * position);\n\n        if (hideOffscreenPages()) {\n            page.setAlpha(position <= -1f || position >= 1f ? 0f : 1f);\n        } else {\n            page.setAlpha(1f);\n        }\n\n        /*final float normalizedposition = Math.abs(Math.abs(position) - 1);\n        page.setAlpha(normalizedposition);*/\n    }\n\n    /**\n     * Called each {@link #transformPage(View, float)} after {@link #onTransform(View, float)}.\n     *\n     * @param page     Apply the transformation to this page\n     * @param position Position of page relative to the current front-and-center position of the pager. 0 is front and\n     *                 center. 1 is one full page position to the right, and -1 is one page position to the left.\n     */\n    protected void onPostTransform(View page, float position) {\n    }\n\n    /**\n     * Same as {@link Math#min(double, double)} without double casting, zero closest to infinity handling, or NaN support.\n     *\n     * @param val\n     * @param min\n     * @return\n     */\n    protected static final float min(float val, float min) {\n        return val < min ? min : val;\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/gu/library/transformer/VerticalStackTransformer.java",
    "content": "package com.gu.library.transformer;\n\nimport android.content.Context;\nimport android.util.Log;\nimport android.view.View;\n\nimport com.gu.library.utils.ScreenUtils;\n\n\n/**\n * Created by Nate on 2016/7/22.\n */\npublic class VerticalStackTransformer extends VerticalBaseTransformer {\n\n    private Context context;\n    private int spaceBetweenFirAndSecWith = 10 * 2;//第一张卡片和第二张卡片宽度差  dp单位\n    private int spaceBetweenFirAndSecHeight = 10;//第一张卡片和第二张卡片高度差   dp单位\n\n    public VerticalStackTransformer(Context context) {\n        this.context = context;\n    }\n\n    public VerticalStackTransformer(Context context, int spaceBetweenFirAndSecWith, int spaceBetweenFirAndSecHeight) {\n        this.context = context;\n        this.spaceBetweenFirAndSecWith = spaceBetweenFirAndSecWith;\n        this.spaceBetweenFirAndSecHeight = spaceBetweenFirAndSecHeight;\n    }\n\n    @Override\n    protected void onTransform(View page, float position) {\n        if (position <= 0.0f) {\n            page.setAlpha(1.0f);\n            Log.e(\"onTransform\", \"position <= 0.0f ==>\" + position);\n            page.setTranslationY(0f);\n            //控制停止滑动切换的时候，只有最上面的一张卡片可以点击\n            page.setClickable(true);\n        } else if (position <= 3.0f) {\n            Log.e(\"onTransform\", \"position <= 3.0f ==>\" + position);\n            float scale = (float) (page.getWidth() - ScreenUtils.dp2px(context, spaceBetweenFirAndSecWith * position)) / (float) (page.getWidth());\n            //控制下面卡片的可见度\n            page.setAlpha(1.0f);\n            //控制停止滑动切换的时候，只有最上面的一张卡片可以点击\n            page.setClickable(false);\n            page.setPivotX(page.getWidth() / 2f);\n            page.setPivotY(page.getHeight() / 2f);\n            page.setScaleX(scale);\n            page.setScaleY(scale);\n            page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - scale) + ScreenUtils.dp2px(context, spaceBetweenFirAndSecHeight) * position);\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/gu/library/utils/ScreenUtils.java",
    "content": "package com.gu.library.utils;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Rect;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.widget.ImageView;\n\n/**\n * Created by Nate on 2015/9/10. 屏幕相关工具类，可以获取屏幕宽高度，还有截取屏幕\n */\npublic class ScreenUtils {\n\n    private ScreenUtils() {\n        /* cannot be instantiated */\n        throw new UnsupportedOperationException(\"cannot be instantiated\");\n    }\n\n    /**\n     * 获得屏幕高度\n     *\n     * @param context\n     * @return\n     */\n    public static int getScreenWidth(Context context) {\n        WindowManager wm = (WindowManager) context\n                .getSystemService(Context.WINDOW_SERVICE);\n        DisplayMetrics outMetrics = new DisplayMetrics();\n        wm.getDefaultDisplay().getMetrics(outMetrics);\n        return outMetrics.widthPixels;\n    }\n\n    /**\n     * 获得屏幕宽度\n     *\n     * @param context\n     * @return\n     */\n    public static int getScreenHeight(Context context) {\n        WindowManager wm = (WindowManager) context\n                .getSystemService(Context.WINDOW_SERVICE);\n        DisplayMetrics outMetrics = new DisplayMetrics();\n        wm.getDefaultDisplay().getMetrics(outMetrics);\n        return outMetrics.heightPixels;\n    }\n\n    /**\n     * 获得状态栏的高度\n     *\n     * @param context\n     * @return\n     */\n    public static int getStatusHeight(Context context) {\n\n        int statusHeight = -1;\n        try {\n            Class<?> clazz = Class.forName(\"com.android.internal.R$dimen\");\n            Object object = clazz.newInstance();\n            int height = Integer.parseInt(clazz.getField(\"status_bar_height\")\n                    .get(object).toString());\n            statusHeight = context.getResources().getDimensionPixelSize(height);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return statusHeight;\n    }\n\n    /**\n     * 获取当前屏幕截图，包含状态栏\n     *\n     * @param activity\n     * @return\n     */\n    public static Bitmap snapShotWithStatusBar(Activity activity) {\n        View view = activity.getWindow().getDecorView();\n        view.setDrawingCacheEnabled(true);\n        view.buildDrawingCache();\n        Bitmap bmp = view.getDrawingCache();\n        int width = getScreenWidth(activity);\n        int height = getScreenHeight(activity);\n        Bitmap bp = null;\n        bp = Bitmap.createBitmap(bmp, 0, 0, width, height);\n        view.destroyDrawingCache();\n        return bp;\n\n    }\n\n    /**\n     * 获取当前屏幕截图，不包含状态栏\n     *\n     * @param activity\n     * @return\n     */\n    public static Bitmap snapShotWithoutStatusBar(Activity activity) {\n        View view = activity.getWindow().getDecorView();\n        view.setDrawingCacheEnabled(true);\n        view.buildDrawingCache();\n        Bitmap bmp = view.getDrawingCache();\n        Rect frame = new Rect();\n        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);\n        int statusBarHeight = frame.top;\n        int width = getScreenWidth(activity);\n        int height = getScreenHeight(activity);\n        Bitmap bp = null;\n        bp = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height\n                - statusBarHeight);\n        view.destroyDrawingCache();\n        return bp;\n    }\n\n    /**\n     * dp转px\n     *\n     * @param context\n     * @param dpVal\n     * @return\n     */\n    public static int dp2px(Context context, float dpVal) {\n        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,\n                dpVal, context.getResources().getDisplayMetrics());\n    }\n\n    /**\n     * sp转px\n     *\n     * @param context\n     * @param spVal\n     * @return\n     */\n    public static int sp2px(Context context, float spVal) {\n        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,\n                spVal, context.getResources().getDisplayMetrics());\n    }\n\n    /**\n     * px转dp\n     *\n     * @param context\n     * @param pxVal\n     * @return\n     */\n    public static float px2dp(Context context, float pxVal) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (pxVal / scale);\n    }\n\n    /**\n     * px转sp\n     *\n     * @param context\n     * @param pxVal\n     * @return\n     */\n    public static float px2sp(Context context, float pxVal) {\n        return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);\n    }\n\n    /**\n     * 动态设置图片宽高\n     */\n    /**\n     * 动态设置图片宽高\n     */\n    public static float[] getBitmapConfiguration(Bitmap bitmap, ImageView imageView, float screenRadio) {\n        int screenWidth = getScreenWidth(imageView.getContext());\n        float rawWidth = 0;\n        float rawHeight = 0;\n        float width = 0;\n        float height = 0;\n        if (bitmap == null) {\n            width = (float) (screenWidth / screenRadio);\n            height = (float) width;\n            imageView.setScaleType(ImageView.ScaleType.FIT_XY);\n        } else {\n            rawWidth = bitmap.getWidth();\n            rawHeight = bitmap.getHeight();\n            if (rawHeight > 10 * rawWidth) {\n                imageView.setScaleType(ImageView.ScaleType.CENTER);\n            } else {\n                imageView.setScaleType(ImageView.ScaleType.FIT_XY);\n            }\n            float radio = rawHeight / rawWidth;\n            width = (float) (screenWidth / screenRadio);\n            height = (float) (radio * width);\n        }\n        return new float[]{width, height};\n    }\n}\n"
  },
  {
    "path": "library/src/test/java/com/gu/library/ExampleUnitTest.java",
    "content": "package com.gu.library;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app', ':library'\n"
  }
]