Beruflich Dokumente
Kultur Dokumente
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
qRI3Y/Tg+yAs44eoKBIB6lxfwGGjlC3NARbWYMdtaO0mtaCKL05QbYyNzLc+ZvQaD4RI4PaClbTW0j6W
lu9bcHL/X0DWICZy6godkKa11Dvbct1D+AxiJ82oKRolRDYK3xAWWhyTxxZWo6uYxjhcZQAcgi49Xrs6
vrqcfY3DIif26F1T5xDWMvyb0p8XiRnRsegZN5hV5qFlsDwZauhfuLq2vrqQ30onYWZ7NlBlHwUX/P9C
wuyctlSAafLmDZbSjRQc2ez+/SPJcvmVCNdH7YskJDGRNyXTQgkY70UDhZ4mz4vs0geWx5DxeK+AM45Q
8dEOnwGM6Y0ukxuwwuqaWDhgZ8FloeJQYbJ8G+x3b2bD74IvigN0mRXanAwFBb/Gm191387RKlSWi03o
OvB96aW56ZzfU5z86PnUY/J2Rr+ao6tS/rU7snY7HoUhRe4LeDStB2M5ziubGVuj0a9beOjrb6o/6sU+
93q1uyLep6uz48OUkmgR/ICbt9Rc7pVaA9Za+akzXy4bfDWjGgvxMfEdxu4ZM83+1wdEuhEHH35cbD18
B6vLd3nJ0k0uVOZ7lZEOTox1y0c8V0+uFfh7uvz91lt9NhOG62CMLf2eVtUHmyu3sif9kGbEzxH2ff3u
J42mNgZGBgYGZkaz+/4EQ8v81XBnkOBhA47/pJEJnmYOCAUEwgCgArZQjYAHjaY2BkYOBg+H8DRDIw/P
8PJIEiKIAVAGP8A/V42j1PMQ4CQQgcuCtMLI3/8Q8m1la+g+Ji5x+u1rdoZ+ITTGx9gMzACVlYZtkZsC
9kfgUs7ys4DAiEw8OyAmsasWDU+y2RWdWrkcAZFwvcTQwZecQnP1SWGv8tHerClooWfqReYoE9M/sG4h
thGDN6cUZPydtcSqXli35vonxCW+lZ7VfbcI/sHHfUKIdY3ExTWW84rDXl3/3ZKh8y2IDJ2fdItvcPrl
0szgAAAAAAJgAmACYALgCSAN4BWgGgAa4B5AI6AtQDJAN8BEoFHAVqBcgGGgcIB+gIUgjmCaIKaAqeCv
gLJgtwDAwMkg4QD2QQShFQEcYTOhPGFCgUZBSQFLwU5hVaFYoWBBagF2YXnBf6GGoZDBmSGhAaMhpUGt
4a/BsuG0obdhugHAIcQBy6HTgdfB2QHbweJh5AHmoe6B8sH+QgJiBYIHogoCC4IMwg4iD2IQwhKiHuIj
4iwCMWI34j1iQ6JHIkwiVYJbQmHCZAJl4mfCaaJqgm+id6J8ooFCieKLQoyikOKTApWinWKjIqcCqqKt
YrIityK8osJCxkLKws7i5ELoguni6sLrouyC7qLzwvgi+8MCYwejCoMOIxKDF6McAx9jIUMygzpDQCNC
Y04DVcNew2YjbSNyI3hjfKOEw4ajjQOgQ6sjryO1Q7/DxiPOI95j7eQDQAAAABAAAAqAC9ABAAAAAAAA
IAAQACABYAAAEAAXYAAAAAeNqFkr1OAkEUhb8RNCEaY2VhNbWJ64L4BxWx02iMf9QsIhiBNbASfQELC9
/DN7DwAbTxCXwYz86OBtHETObOmXvO/dk7C8zyQA6TLwAd7QwbFnTL8BTz3HqcY4N7j/NYXjyeluLD4x
msOfG4wJJJPJ5j2Tx6/Mqiefb4jdC8s0PMNXcMuKSt2omyP2mXCCnKWiKxlho34mJ6NHQmUjcJnL+rZc
fih+7W0tnSOZI9l/JAsVlUTF/ckfxt5ewq34A1KUKtMlVO2aMuffVXzMpE1CRvJ/gz18FQipS3P6occi
xPehv3dlzGLN9IfNFxAZuyVfftV8qZai7k7SpzpBkFrLu9pVwltv/te1f+luuoJrYn/9BNq+/ml+g9Kq
xqNf6cedN56tJH6iKtkHx3WnfTtuy7Ota/YKj+K8Jl2ZL+pK/XLX8CxEdgJnjabdL5101lGMbx832ToZ
k004DSIL37ee5n762EfZzeFJWi0ISK0oBEJZI0SAnNIyll9df0L5Wc7+sne62zrh+ec3/2fq5190Z6Z5
9//+ml3vmev8/86I0wwgVM4EImMonJTOEiLuYSLuUyLucKpjKNK5nOVVzNNVzLdVzPDcxgJjdyEzdzC7
OYzRxu5Tbmcjt3cCd3MY+7mc89jFKRyASFmoaWBdzLfSzkfhaxmCV09FnKgAcY40GW8RAPs5wVPMKjPM
ZKHucJVrGaJ3mKNaxlHU/zDM/yHM+zng1s5AVe5CU2sZmXeYUtvMprvM4bbGUb23mTHbzFTnbxNu/wLr
t5jz3s5X328QH7+ZADfMTHfMKnHOQzDvE5X3CYLznCUY7xFV/zDd/yHd/zAz/yEz/zC79ynBP8xkl+5w
9O8Sd/cXrSrq1bUhlb+n+OVaOjZmUmM5thFrM2G7M1F5zNpJf0kl7SS3pJL+klvaSXxr1Kr9Kr9Cq9Sq
/Sq/QqvUqv0kt6SS/pJb2kl4Ze9j7Z+2Tvk71P9j55dPz/tdmYw/eHTuiETuiETuiETujEOWd4j7CXsJ
ewl7CXsJewl7CXsJewl7CXsJewl7CXsJewl7CXSHpJL+klvayX9bJe1st6WS/rZb2sl/VCL/RCL/RCL/
RCL/RCL/SKXtErekWv6BW9olf0il7Rq/VqvVqv1qv1ar1ar9ar9Wq9Rq/Ra/QavUav0WuGXnHPintW3L
PinrWet563nrfnzsMsZm025vA7W/evdf9a9691/1r3r3X/Wvevdf/azvnO+c75zvnO+c75zvlufN7v6I
Z9tX29vl5fr6/X1+vr9fUGzg2cGzg3yP8BqrOmDgAAALgB/4WwAY0AS7AIUFixAQGOWbFGBitYIbAQWU
uwFFJYIbCAWR2wBitcWFmwFCsAAAABUyBBkgAA) format('woff'),
url(/i/noticons/noticons-regular-webfont.ttf) format('truetype'
),
url(/i/noticons/noticons-regular-webfont.svg#NoticonsRegular) f
ormat('svg');
font-weight: normal;
font-style: normal;
}
/**
* All noticons
*/
/*
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f100';
'\f101';
'\f102';
'\f103';
'\f104';
'\f105';
'\f106';
'\f107';
'\f108';
'\f109';
}
}
}
}
}
}
}
}
}
}
/* Social icons */
.noticon-github:before {
.noticon-dribbble:before {
.noticon-twitter:before {
.noticon-facebook:before {
.noticon-facebook-alt:before {
.noticon-wordpress:before {
.noticon-googleplus:before {
.noticon-linkedin:before {
.noticon-linkedin-alt:before {
.noticon-pinterest:before {
.noticon-pinterest-alt:before {
.noticon-flickr:before {
.noticon-vimeo:before {
.noticon-youtube:before {
.noticon-tumblr:before {
.noticon-instagram:before {
.noticon-codepen:before {
.noticon-polldaddy:before {
.noticon-googleplus-alt:before {
.noticon-path:before {
.noticon-skype:before {
.noticon-digg:before {
.noticon-reddit:before {
.noticon-stumbleupon:before {
.noticon-pocket:before {
.noticon-dropbox:before {
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f200';
'\f201';
'\f202';
'\f203';
'\f204';
'\f205';
'\f206';
'\f207';
'\f208';
'\f209';
'\f210';
'\f211';
'\f212';
'\f213';
'\f214';
'\f215';
'\f216';
'\f217';
'\f218';
'\f219';
'\f220';
'\f221';
'\f222';
'\f223';
'\f224';
'\f225';
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
/* Meta icons */
.noticon-comment:before {
.noticon-category:before {
.noticon-tag:before {
.noticon-time:before {
.noticon-user:before {
.noticon-day:before {
.noticon-week:before {
.noticon-month:before {
.noticon-pinned:before {
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f300';
'\f301';
'\f302';
'\f303';
'\f304';
'\f305';
'\f306';
'\f307';
'\f308';
}
}
}
}
}
}
}
}
}
/* Other icons */
.noticon-search:before {
.noticon-unzoom:before {
.noticon-zoom:before {
.noticon-show:before {
.noticon-hide:before {
.noticon-close:before {
.noticon-close-alt:before {
.noticon-trash:before {
.noticon-star:before {
.noticon-home:before {
.noticon-mail:before {
.noticon-edit:before {
.noticon-reply:before {
.noticon-feed:before {
.noticon-warning:before {
.noticon-share:before {
.noticon-attachment:before {
.noticon-location:before {
.noticon-checkmark:before {
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f400';
'\f401';
'\f402';
'\f403';
'\f404';
'\f405';
'\f406';
'\f407';
'\f408';
'\f409';
'\f410';
'\f411';
'\f412';
'\f413';
'\f414';
'\f415';
'\f416';
'\f417';
'\f418';
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
.noticon-menu:before {
.noticon-refresh:before {
.noticon-minimize:before {
.noticon-maximize:before {
.noticon-404:before {
.noticon-spam:before {
.noticon-summary:before {
.noticon-cloud:before {
.noticon-key:before {
.noticon-dot:before {
.noticon-next:before {
.noticon-previous:before {
.noticon-expand:before {
.noticon-collapse:before {
.noticon-dropdown:before {
.noticon-dropdown-left:before {
.noticon-top:before {
.noticon-draggable:before {
.noticon-phone:before {
.noticon-send-to-phone:before {
.noticon-plugin:before {
.noticon-cloud-download:before {
.noticon-cloud-upload:before {
.noticon-external:before {
.noticon-document:before {
.noticon-book:before {
.noticon-cog:before {
.noticon-unapprove:before {
.noticon-cart:before {
.noticon-pause:before {
.noticon-stop:before {
.noticon-skip-back:before {
.noticon-skip-ahead:before {
.noticon-play:before {
.noticon-tablet:before {
.noticon-send-to-tablet:before {
.noticon-info:before {
.noticon-notice:before {
.noticon-help:before {
.noticon-fastforward:before {
.noticon-rewind:before {
.noticon-portfolio:before {
.noticon-heart:before {
.noticon-code:before {
.noticon-subscribe:before {
.noticon-unsubscribe:before {
.noticon-subscribed:before {
.noticon-reply-alt:before {
.noticon-reply-single:before {
.noticon-flag:before {
.noticon-print:before {
.noticon-lock:before {
.noticon-bold:before {
.noticon-italic:before {
.noticon-picture:before {
.noticon-fullscreen:before {
.noticon-website:before {
.noticon-ellipsis:before {
/* Generic shapes */
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f419';
'\f420';
'\f421';
'\f422';
'\f423';
'\f424';
'\f425';
'\f426';
'\f427';
'\f428';
'\f429';
'\f430';
'\f431';
'\f432';
'\f433';
'\f434';
'\f435';
'\f436';
'\f437';
'\f438';
'\f439';
'\f440';
'\f441';
'\f442';
'\f443';
'\f444';
'\f445';
'\f446';
'\f447';
'\f448';
'\f449';
'\f450';
'\f451';
'\f452';
'\f453';
'\f454';
'\f455';
'\f456';
'\f457';
'\f458';
'\f459';
'\f460';
'\f461';
'\f462';
'\f463';
'\f464';
'\f465';
'\f466';
'\f467';
'\f468';
'\f469';
'\f470';
'\f471';
'\f472';
'\f473';
'\f474';
'\f475';
'\f476';
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
.noticon-uparrow:before {
.noticon-rightarrow:before {
.noticon-downarrow:before {
.noticon-leftarrow:before {
content:
content:
content:
content:
'\f500';
'\f501';
'\f502';
'\f503';
}
}
}
}
/* WPCOM specific */
.noticon-notification:before {
.noticon-follow:before {
.noticon-unfollow:before {
.noticon-following:before {
.noticon-trophy:before {
.noticon-reblog:before {
.noticon-milestone:before {
.noticon-compact:before {
.noticon-gridview:before {
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f800';
'\f801';
'\f802';
'\f803';
'\f804';
'\f805';
'\f806';
'\f807';
'\f808';
}
}
}
}
}
}
}
}
}
.noticon-trapper:before {
.noticon-spike:before {
.noticon-promoted:before {
.noticon-wordads:before {
.noticon-atsign:before {
.noticon-automattic:before {
.noticon-automattic-ring:before {
.noticon-automattic-blip:before {
.noticon-bullseye:before {
.noticon-lightbulb:before {
.noticon-reader:before {
.noticon-reader-alt:before {
.noticon-gift:before {
.noticon-bullhorn:before {
.noticon-eventbrite:before {
.noticon-colors:before {
.noticon-features:before {
.noticon-layouts:before {
.noticon-price:before {
.noticon-types:before {
.noticon-localization:before {
.noticon-add:before {
.noticon-art:before {
.noticon-fonts:before {
.noticon-title:before {
.noticon-gravatar:before {
.noticon-vaultpress:before {
.noticon-akismet:before {
.noticon-jetpack:before {
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f810';
'\f811';
'\f812';
'\f813';
'\f814';
'\f815';
'\f816';
'\f817';
'\f8a0';
'\f8a1';
'\f8a2';
'\f8a3';
'\f8a4';
'\f8a5';
'\f8a6';
'\f8a7';
'\f8a8';
'\f8a9';
'\f8b0';
'\f8b1';
'\f8b2';
'\f8b3';
'\f8b4';
'\f8b5';
'\f8b6';
'\f8d0';
'\f8d1';
'\f8d2';
'\f8d3';
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
/**
* Deprecated noticon names
*/
.noticon-text:before {
.noticon-like:before {
.noticon-stats:before {
.noticon-wp:before {
.noticon-wpcom:before {
.noticon-alert:before {
.noticon-up:before {
.noticon-right:before {
.noticon-down:before {
.noticon-left:before {
content:
content:
content:
content:
content:
content:
content:
content:
content:
content:
'\f100';
'\f408';
'\f806';
'\f205';
'\f205';
'\f414';
'\f500';
'\f501';
'\f502';
'\f503';
}
}
}
}
}
}
}
}
}
}
.noticon-close-small:before {
.noticon-arrow-right:before {
.noticon-arrow-left:before {
.noticon-camera:before {
.noticon-gplus:before {
.noticon-eye:before {
.noticon-eye-cross:before {
content:
content:
content:
content:
content:
content:
content:
'\f405';
'\f429';
'\f430';
'\f102';
'\f206';
'\f403';
'\f404';
}
}
}
}
}
}
}
android.content.Context;
android.content.res.Resources;
android.content.res.TypedArray;
android.database.DataSetObserver;
android.graphics.Canvas;
android.graphics.Rect;
android.graphics.drawable.Drawable;
android.os.Build;
android.os.Bundle;
android.os.Parcel;
android.os.Parcelable;
android.os.SystemClock;
android.support.v4.os.ParcelableCompat;
android.support.v4.os.ParcelableCompatCreatorCallbacks;
android.support.v4.view.accessibility.AccessibilityEventCompat;
android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
android.support.v4.view.accessibility.AccessibilityRecordCompat;
android.support.v4.widget.EdgeEffectCompat;
android.util.AttributeSet;
android.util.Log;
android.view.FocusFinder;
android.view.Gravity;
android.view.KeyEvent;
android.view.MotionEvent;
android.view.SoundEffectConstants;
android.view.VelocityTracker;
android.view.View;
android.view.ViewConfiguration;
android.view.ViewGroup;
android.view.ViewParent;
android.view.accessibility.AccessibilityEvent;
android.view.animation.Interpolator;
android.widget.Scroller;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* Layout manager that allows the user to flip left and right
* through pages of data. You supply an implementation of a
* {@link PagerAdapter} to generate the pages that the view shows.
*
* <p>Note this class is currently under early design and
* development. The API will likely change in later updates of
* the compatibility library, requiring changes to the source code
* of apps when they are compiled against the newer version.</p>
*
* <p>ViewPager is most often used in conjunction with {@link android.app.Fragme
nt},
* which is a convenient way to supply and manage the lifecycle of each page.
* There are standard adapters implemented for using fragments with the ViewPage
r,
* which cover the most common use cases. These are
* {@link android.support.v4.app.FragmentPagerAdapter} and
* {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these
* classes have simple code showing how to build a full user interface
* with them.
*
* <p>Here is a more complicated example of ViewPager, using it in conjuction
* with {@link android.app.ActionBar} tabs. You can find other examples of usin
g
* ViewPager in the API 4+ Support Demos and API 13+ Support Demos sample code.
*
* {@sample development/samples/Support13Demos/src/com/example/android/supportv1
3/app/ActionBarTabsPager.java
*
complete}
*/
public class ViewPager extends ViewGroup {
private static final String TAG = "ViewPager";
private static final boolean DEBUG = false;
private static final boolean USE_CACHE = false;
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
private static final int MAX_SETTLE_DURATION = 600; // ms
private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
private static final int DEFAULT_GUTTER_SIZE = 16; // dips
private static final int MIN_FLING_VELOCITY = 400; // dips
private static final int[] LAYOUT_ATTRS = new int[] {
android.R.attr.layout_gravity
};
/**
* Used to track what the expected number of items in the adapter should be.
* If the app changes this when we don't expect it, we'll throw a big obnoxi
ous exception.
*/
private int mExpectedAdapterCount;
static class ItemInfo {
Object object;
int position;
boolean scrolling;
float widthFactor;
float offset;
PagerAdapter mAdapter;
int mCurItem; // Index of currently displayed page.
int mRestoredCurItem = -1;
Parcelable mRestoredAdapterState = null;
ClassLoader mRestoredClassLoader = null;
Scroller mScroller;
PagerObserver mObserver;
private
private
private
private
int mPageMargin;
Drawable mMarginDrawable;
int mTopPageBounds;
int mBottomPageBounds;
boolean mIsBeingDragged;
boolean mIsUnableToDrag;
boolean mIgnoreGutter;
int mDefaultGutterSize;
int mGutterSize;
int mTouchSlop;
private
private
private
private
private
OnPageChangeListener mOnPageChangeListener;
OnPageChangeListener mInternalPageChangeListener;
OnAdapterChangeListener mAdapterChangeListener;
PageTransformer mPageTransformer;
Method mSetChildrenDrawingOrderEnabled;
n.
/**
* Indicates that the pager is in the process of settling to a final positio
*/
public static final int SCROLL_STATE_SETTLING = 2;
private final Runnable mEndScrollRunnable = new Runnable() {
public void run() {
setScrollState(SCROLL_STATE_IDLE);
populate();
}
};
private int mScrollState = SCROLL_STATE_IDLE;
/**
* Callback interface for responding to changing state of the selected page.
*/
public interface OnPageChangeListener {
/**
* This method will be invoked when the current page is scrolled, either
as part
user
/**
* Called when the scroll state changes. Useful for discovering when the
* begins dragging, when the pager is automatically settling to the curr
ent page,
/**
* Simple implementation of the {@link OnPageChangeListener} interface with
stub
ide
er {
@Override
public void onPageScrolled(int position, float positionOffset, int posit
ionOffsetPixels) {
// This space for rent
}
@Override
public void onPageSelected(int position) {
// This space for rent
}
@Override
public void onPageScrollStateChanged(int state) {
// This space for rent
}
/**
* A PageTransformer is invoked whenever a visible/attached page is scrolled
*/
public void transformPage(View page, float position);
/**
* Used internally to monitor when adapters are switched.
*/
interface OnAdapterChangeListener {
public void onAdapterChanged(PagerAdapter oldAdapter, PagerAdapter newAd
apter);
}
as
/**
* Used internally to tag special types of child views that should be added
* pager decorations by default.
*/
interface Decor {}
public ViewPager(Context context) {
super(context);
initViewPager();
}
public ViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
initViewPager();
}
void initViewPager() {
setWillNotDraw(false);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setFocusable(true);
final Context context = getContext();
mScroller = new Scroller(context, sInterpolator);
final ViewConfiguration configuration = ViewConfiguration.get(context);
final float density = context.getResources().getDisplayMetrics().density
ion);
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configurat
mMinimumVelocity = (int) (MIN_FLING_VELOCITY * density);
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mLeftEdge = new EdgeEffectCompat(context);
mRightEdge = new EdgeEffectCompat(context);
mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);
mCloseEnough = (int) (CLOSE_ENOUGH * density);
mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
ViewCompat.setAccessibilityDelegate(this, new MyAccessibilityDelegate())
if (ViewCompat.getImportantForAccessibility(this)
== ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
ViewCompat.setImportantForAccessibility(this,
ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
@Override
protected void onDetachedFromWindow() {
removeCallbacks(mEndScrollRunnable);
super.onDetachedFromWindow();
}
private void setScrollState(int newState) {
if (mScrollState == newState) {
return;
}
mScrollState = newState;
if (mPageTransformer != null) {
// PageTransformers can do complex things that benefit from hardware
layers.
enableLayers(newState != SCROLL_STATE_IDLE);
}
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrollStateChanged(newState);
}
/**
* Set a PagerAdapter that will supply views for this pager as needed.
*
* @param adapter Adapter to use
*/
public void setAdapter(PagerAdapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mObserver);
mAdapter.startUpdate(this);
for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
mAdapter.destroyItem(this, ii.position, ii.object);
}
mAdapter.finishUpdate(this);
mItems.clear();
removeNonDecorViews();
mCurItem = 0;
scrollTo(0, 0);
}
final PagerAdapter oldAdapter = mAdapter;
mAdapter = adapter;
mExpectedAdapterCount = 0;
r);
if (mAdapter != null) {
if (mObserver == null) {
mObserver = new PagerObserver();
}
mAdapter.registerDataSetObserver(mObserver);
mPopulatePending = false;
final boolean wasFirstLayout = mFirstLayout;
mFirstLayout = true;
mExpectedAdapterCount = mAdapter.getCount();
if (mRestoredCurItem >= 0) {
mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoade
setCurrentItemInternal(mRestoredCurItem, false, true);
mRestoredCurItem = -1;
mRestoredAdapterState = null;
mRestoredClassLoader = null;
} else if (!wasFirstLayout) {
populate();
} else {
requestLayout();
}
* @param smoothScroll True to smoothly scroll to the new item, false to tra
nsition immediately
*/
public void setCurrentItem(int item, boolean smoothScroll) {
mPopulatePending = false;
setCurrentItemInternal(item, smoothScroll, false);
}
public int getCurrentItem() {
return mCurItem;
}
{
}
private void scrollToItem(int item, boolean smoothScroll, int velocity,
boolean dispatchSelected) {
final ItemInfo curInfo = infoForPosition(item);
int destX = 0;
if (curInfo != null) {
final int width = getClientWidth();
destX = (int) (width * Math.max(mFirstOffset,
Math.min(curInfo.offset, mLastOffset)));
}
if (smoothScroll) {
smoothScrollTo(destX, 0, velocity);
if (dispatchSelected && mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(item);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
mInternalPageChangeListener.onPageSelected(item);
}
} else {
if (dispatchSelected && mOnPageChangeListener != null) {
mOnPageChangeListener.onPageSelected(item);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
mInternalPageChangeListener.onPageSelected(item);
}
completeScroll(false);
scrollTo(destX, 0);
pageScrolled(destX);
}
}
/**
* Set a listener that will be invoked whenever the page changes or is incre
mentally
* scrolled. See {@link OnPageChangeListener}.
*
* @param listener Listener to set
*/
public void setOnPageChangeListener(OnPageChangeListener listener) {
mOnPageChangeListener = listener;
}
/**
* Set a {@link PageTransformer} that will be called for each attached page
whenever
* the scroll position is changed. This allows the application to apply cust
om property
* transformations to each page, overriding the default sliding look and fee
l.
*
* <p><em>Note:</em> Prior to Android 3.0 the property animation APIs did no
t exist.
* As a result, setting a PageTransformer prior to Android 3.0 (API 11) will
have no effect.</p>
*
* @param reverseDrawingOrder true if the supplied PageTransformer requires
page views
*
to be drawn from last to first instead of firs
t to last.
ry.
/**
* Set a separate OnPageChangeListener for internal use by the support libra
*
* @param listener Listener to set
* @return The old listener that was set, if any.
*/
OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener list
ener) {
OnPageChangeListener oldListener = mInternalPageChangeListener;
mInternalPageChangeListener = listener;
return oldListener;
/**
* Returns the number of pages that will be retained to either side of the
* current page in the view hierarchy in an idle state. Defaults to 1.
*
* @return How many pages will be kept offscreen on either side
* @see #setOffscreenPageLimit(int)
*/
public int getOffscreenPageLimit() {
return mOffscreenPageLimit;
}
/**
* Set the number of pages that should be retained to either side of
* current page in the view hierarchy in an idle state. Pages beyond
* limit will be recreated from the adapter when needed.
*
* <p>This is offered as an optimization. If you know in advance the
* of pages you will need to support or have lazy-loading mechanisms
the
this
number
in plac
* on your pages, tweaking this setting can have benefits in perceived smoot
hness
mPageMargin = marginPixels;
final int width = getWidth();
recomputeScrollPosition(width, width, marginPixels, oldMargin);
}
requestLayout();
/**
* Return the margin between pages.
*
* @return The size of the margin in pixels
*/
public int getPageMargin() {
return mPageMargin;
}
/**
* Set a drawable that will be used to fill the margin between pages.
*
* @param d Drawable to display between pages
*/
public void setPageMarginDrawable(Drawable d) {
mMarginDrawable = d;
if (d != null) refreshDrawableState();
setWillNotDraw(d == null);
invalidate();
}
/**
* Set a drawable that will be used to fill the margin between pages.
*
* @param resId Resource ID of a drawable to display between pages
*/
public void setPageMarginDrawable(int resId) {
setPageMarginDrawable(getContext().getResources().getDrawable(resId));
}
@Override
protected boolean verifyDrawable(Drawable who) {
return super.verifyDrawable(who) || who == mMarginDrawable;
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
final Drawable d = mMarginDrawable;
if (d != null && d.isStateful()) {
d.setState(getDrawableState());
}
}
// We want the duration of the page snap animation to be influenced by the d
istance that
// the screen has to travel, however, we don't want this duration to be effe
cted in a
// purely linear fashion. Instead, we use this method to moderate the effect
that the distance
// of travel has on the overall snap duration.
float distanceInfluenceForSnapDuration(float f) {
/**
* Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
*
* @param x the number of pixels to scroll by on the X axis
* @param y the number of pixels to scroll by on the Y axis
*/
void smoothScrollTo(int x, int y) {
smoothScrollTo(x, y, 0);
}
/**
* Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
*
* @param x the number of pixels to scroll by on the X axis
* @param y the number of pixels to scroll by on the Y axis
* @param velocity the velocity associated with a fling, if applicable. (0 o
therwise)
*/
void smoothScrollTo(int x, int y, int velocity) {
if (getChildCount() == 0) {
// Nothing to do.
setScrollingCacheEnabled(false);
return;
}
int sx = getScrollX();
int sy = getScrollY();
int dx = x - sx;
int dy = y - sy;
if (dx == 0 && dy == 0) {
completeScroll(false);
populate();
setScrollState(SCROLL_STATE_IDLE);
return;
}
setScrollingCacheEnabled(true);
setScrollState(SCROLL_STATE_SETTLING);
final
final
final
final
gin);
int duration = 0;
velocity = Math.abs(velocity);
if (velocity > 0) {
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
} else {
final float pageWidth = width * mAdapter.getPageWidth(mCurItem);
final float pageDelta = (float) Math.abs(dx) / (pageWidth + mPageMar
duration = (int) ((pageDelta + 1) * 100);
}
duration = Math.min(duration, MAX_SETTLE_DURATION);
1));
needPopulate = true;
}
continue;
if (ii.position != newPos) {
if (ii.position == mCurItem) {
ii.position = newPos;
needPopulate = true;
if (isUpdating) {
mAdapter.finishUpdate(this);
}
Collections.sort(mItems, COMPARATOR);
if (needPopulate) {
// Reset our known page widths; populate will recompute them.
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.isDecor) {
lp.widthFactor = 0.f;
}
}
void populate() {
populate(mCurItem);
}
void populate(int newCurrentItem) {
ItemInfo oldCurInfo = null;
int focusDirection = View.FOCUS_FORWARD;
if (mCurItem != newCurrentItem) {
focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT : View
.FOCUS_LEFT;
oldCurInfo = infoForPosition(mCurItem);
mCurItem = newCurrentItem;
}
if (mAdapter == null) {
sortChildDrawingOrder();
return;
}
//
//
//
//
if
//
//
//
if
}
mAdapter.startUpdate(this);
final
final
final
final
int
int
int
int
pageLimit = mOffscreenPageLimit;
startPos = Math.max(0, mCurItem - pageLimit);
N = mAdapter.getCount();
endPos = Math.min(N-1, mCurItem + pageLimit);
if (N != mExpectedAdapterCount) {
String resName;
try {
resName = getResources().getResourceName(getId());
} catch (Resources.NotFoundException e) {
resName = Integer.toHexString(getId());
}
throw new IllegalStateException("The application's PagerAdapter chan
ged the adapter's" +
" contents without calling PagerAdapter#notifyDataSetChanged
!" +
" Expected adapter item count: " + mExpectedAdapterCount + "
, found: " + N +
" Pager id: " + resName +
" Pager class: " + getClass() +
" Problematic adapter: " + mAdapter.getClass());
}
// Locate the currently focused item or add it if needed.
int curIndex = -1;
ItemInfo curItem = null;
for (curIndex = 0; curIndex < mItems.size(); curIndex++) {
final ItemInfo ii = mItems.get(curIndex);
if (ii.position >= mCurItem) {
if (ii.position == mCurItem) curItem = ii;
break;
}
}
if (curItem == null && N > 0) {
curItem = addNewItem(mCurItem, curIndex);
}
//
//
//
if
if (ii == null) {
break;
}
if (pos == ii.position && !ii.scrolling) {
mItems.remove(itemIndex);
mAdapter.destroyItem(this, pos, ii.object);
if (DEBUG) {
Log.i(TAG, "populate() - destroyItem() with pos: " +
pos +
" + pos +
x) : null;
null;
null;
}
} else if (ii != null && pos == ii.position) {
extraWidthLeft += ii.widthFactor;
itemIndex--;
ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
} else {
ii = addNewItem(pos, itemIndex + 1);
extraWidthLeft += ii.widthFactor;
curIndex++;
ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
}
}
}
if (DEBUG) {
Log.i(TAG, "Current page list:");
for (int i=0; i<mItems.size(); i++) {
Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);
}
}
mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object
: null);
mAdapter.finishUpdate(this);
width.
}
}
sortChildDrawingOrder();
if (hasFocus()) {
View currentFocused = findFocus();
ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocuse
d) : null;
if (ii == null || ii.position != mCurItem) {
for (int i=0; i<getChildCount(); i++) {
View child = getChildAt(i);
ii = infoForChild(child);
if (ii != null && ii.position == mCurItem) {
if (child.requestFocus(focusDirection)) {
break;
}
}
}
}
}
}
private void sortChildDrawingOrder() {
if (mDrawingOrder != DRAW_ORDER_DEFAULT) {
if (mDrawingOrderedChildren == null) {
mDrawingOrderedChildren = new ArrayList<View>();
} else {
mDrawingOrderedChildren.clear();
}
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
mDrawingOrderedChildren.add(child);
}
Collections.sort(mDrawingOrderedChildren, sPositionComparator);
mLastOffset = curItem.position == N - 1 ?
curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
// Previous pages
for (int i = curIndex - 1; i >= 0; i--, pos--) {
final ItemInfo ii = mItems.get(i);
while (pos > ii.position) {
offset -= mAdapter.getPageWidth(pos--) + marginOffset;
}
offset -= ii.widthFactor + marginOffset;
ii.offset = offset;
if (ii.position == 0) mFirstOffset = offset;
}
offset = curItem.offset + curItem.widthFactor + marginOffset;
pos = curItem.position + 1;
// Next pages
for (int i = curIndex + 1; i < itemCount; i++, pos++) {
final ItemInfo ii = mItems.get(i);
while (pos < ii.position) {
offset += mAdapter.getPageWidth(pos++) + marginOffset;
}
if (ii.position == N - 1) {
mLastOffset = offset + ii.widthFactor - 1;
}
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
}
mNeedCalculatePageOffsets = false;
/**
* This is the persistent state that is saved by ViewPager. Only needed
* if you are creating a sublass of ViewPager that must save its own
* state, in which case it should implement a subclass of this which
* contains that state.
*/
public static class SavedState extends BaseSavedState {
int position;
Parcelable adapterState;
ClassLoader loader;
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(position);
out.writeParcelable(adapterState, flags);
@Override
public String toString() {
return "FragmentPager.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " position=" + position + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
= ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbac
ks<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in, ClassLoader lo
ader) {
return new SavedState(in, loader);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
});
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.position = mCurItem;
if (mAdapter != null) {
ss.adapterState = mAdapter.saveState();
}
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
if (mAdapter != null) {
mAdapter.restoreState(ss.adapterState, ss.loader);
setCurrentItemInternal(ss.position, false, true);
} else {
mRestoredCurItem = ss.position;
mRestoredAdapterState = ss.adapterState;
mRestoredClassLoader = ss.loader;
}
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (!checkLayoutParams(params)) {
params = generateLayoutParams(params);
}
final LayoutParams lp = (LayoutParams) params;
lp.isDecor |= child instanceof Decor;
if (mInLayout) {
if (lp != null && lp.isDecor) {
throw new IllegalStateException("Cannot add pager decor view dur
ing layout");
}
lp.needsMeasure = true;
addViewInLayout(child, index, params);
} else {
super.addView(child, index, params);
}
if (USE_CACHE) {
if (child.getVisibility() != GONE) {
child.setDrawingCacheEnabled(mScrollingCacheEnabled);
} else {
child.setDrawingCacheEnabled(false);
}
}
@Override
public void removeView(View view) {
if (mInLayout) {
removeViewInLayout(view);
} else {
super.removeView(view);
}
}
ItemInfo infoForChild(View child) {
for (int i=0; i<mItems.size(); i++) {
ItemInfo ii = mItems.get(i);
if (mAdapter.isViewFromObject(child, ii.object)) {
return ii;
}
}
return null;
}
ItemInfo infoForAnyChild(View child) {
ViewParent parent;
while ((parent=child.getParent()) != this) {
if (parent == null || !(parent instanceof View)) {
return null;
}
child = (View)parent;
}
return infoForChild(child);
);
SK;
/*
* Make sure all children have been properly measured. Decor views first
* Right now we cheat and make this less complicated by assuming decor
* views won't intersect. We will pin to edges based on gravity.
*/
int size = getChildCount();
for (int i = 0; i < size; ++i) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp != null && lp.isDecor) {
final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MA
ravity.BOTTOM;
= Gravity.RIGHT;
if (consumeVertical) {
widthMode = MeasureSpec.EXACTLY;
} else if (consumeHorizontal) {
heightMode = MeasureSpec.EXACTLY;
}
int widthSize = childWidthSize;
int heightSize = childHeightSize;
if (lp.width != LayoutParams.WRAP_CONTENT) {
widthMode = MeasureSpec.EXACTLY;
if (lp.width != LayoutParams.FILL_PARENT) {
widthSize = lp.width;
}
}
if (lp.height != LayoutParams.WRAP_CONTENT) {
heightMode = MeasureSpec.EXACTLY;
if (lp.height != LayoutParams.FILL_PARENT) {
heightSize = lp.height;
}
}
final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize,
widthMode);
e, heightMode);
child.measure(widthSpec, heightSpec);
if (consumeVertical) {
childHeightSize -= child.getMeasuredHeight();
} else if (consumeHorizontal) {
childWidthSize -= child.getMeasuredWidth();
}
.EXACTLY);
child.measure(widthSpec, mChildHeightMeasureSpec);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
progress.
Passed();
scrollTo(newOffsetPixels, getScrollY());
if (!mScroller.isFinished()) {
// We now return to your regularly scheduled scroll, already in
final int newDuration = mScroller.getDuration() - mScroller.time
ItemInfo targetInfo = infoForPosition(mCurItem);
mScroller.startScroll(newOffsetPixels, 0,
(int) (targetInfo.offset * width), 0, newDuration);
}
} else {
final ItemInfo ii = infoForPosition(mCurItem);
final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOff
set) : 0;
final int scrollPos = (int) (scrollOffset *
(width - getPaddingLeft() - getPaddingR
ight()));
if (scrollPos != getScrollX()) {
completeScroll(false);
scrollTo(scrollPos, getScrollY());
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
int width = r - l;
int height = b - t;
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
SK;
()) / 2,
Width();
t()) / 2,
dHeight();
}
switch (vgrav) {
default:
childTop = paddingTop;
break;
case Gravity.TOP:
childTop = paddingTop;
paddingTop += child.getMeasuredHeight();
break;
case Gravity.CENTER_VERTICAL:
childTop = Math.max((height - child.getMeasuredHeigh
paddingTop);
break;
case Gravity.BOTTOM:
childTop = height - paddingBottom - child.getMeasure
paddingBottom += child.getMeasuredHeight();
break;
}
childLeft += scrollX;
child.layout(childLeft, childTop,
childLeft + child.getMeasuredWidth(),
childTop + child.getMeasuredHeight());
decorCount++;
if (mFirstLayout) {
scrollToItem(mCurItem, false, 0, false);
}
mFirstLayout = false;
@Override
public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
int oldX = getScrollX();
int oldY = getScrollY();
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
if (!pageScrolled(x)) {
mScroller.abortAnimation();
scrollTo(0, y);
}
}
final
final
final
final
final
final
ItemInfo ii = infoForCurrentScrollPosition();
int width = getClientWidth();
int widthWithMargin = width + mPageMargin;
float marginOffset = (float) mPageMargin / width;
int currentPage = ii.position;
float pageOffset = (((float) xpos / width) - ii.offset) /
(ii.widthFactor + marginOffset);
final int offsetPixels = (int) (pageOffset * widthWithMargin);
mCalledSuper = false;
onPageScrolled(currentPage, pageOffset, offsetPixels);
if (!mCalledSuper) {
throw new IllegalStateException(
"onPageScrolled did not call superclass implementation");
}
return true;
/**
* This method will be invoked when the current page is scrolled, either as
part
* @param offset Value from [0, 1) indicating the offset from the page at po
sition.
* @param offsetPixels Value in pixels indicating the offset from position.
*/
protected void onPageScrolled(int position, float offset, int offsetPixels)
{
// Offset any decor views if needed - keep them on-screen at all times.
if (mDecorChildCount > 0) {
final int scrollX = getScrollX();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
final int width = getWidth();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.isDecor) continue;
final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
int childLeft = 0;
switch (hgrav) {
default:
childLeft = paddingLeft;
break;
case Gravity.LEFT:
childLeft = paddingLeft;
paddingLeft += child.getWidth();
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = Math.max((width - child.getMeasuredWidth())
/ 2,
paddingLeft);
break;
case Gravity.RIGHT:
childLeft = width - paddingRight - child.getMeasuredWidt
h();
paddingRight += child.getMeasuredWidth();
break;
}
childLeft += scrollX;
ixels);
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels)
}
if (mInternalPageChangeListener != null) {
mInternalPageChangeListener.onPageScrolled(position, offset, offsetP
}
if (mPageTransformer != null) {
final int scrollX = getScrollX();
final int childCount = getChildCount();
mCalledSuper = true;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onMotionEvent will be called and we do the actual
* scrolling there.
*/
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
UP) {
tent.
/*
* Locally do absolute value. mLastMotionY is set to the y value
* of the down event.
*/
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
// If we don't have a valid id, the touch down wasn't on con
}
break;
mCloseEnough) {
mScroller.computeScrollOffset();
if (mScrollState == SCROLL_STATE_SETTLING &&
Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) >
// Let the user 'catch' the pager as it animates.
mScroller.abortAnimation();
mPopulatePending = false;
populate();
mIsBeingDragged = true;
setScrollState(SCROLL_STATE_DRAGGING);
} else {
completeScroll(false);
mIsBeingDragged = false;
}
if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMot
ionY
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
/*
* The only time we want to intercept motion events is if we are in the
* drag mode.
*/
return mIsBeingDragged;
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mFakeDragging) {
// A fake drag is in progress already, ignore this real one
// but still eat the touch events.
// (It is likely that the user is multi-touching the screen.)
return true;
}
{
}
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
final int pointerIndex = MotionEventCompat.findPointerIndex(
ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff="
+ xDiff + "," + yDiff);
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX
+ mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
}
}
// Not else! Note that mIsBeingDragged can be set above.
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = MotionEventCompat.findPointer
Index(
ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, activePointerInde
x);
needsInvalidate |= performDrag(x);
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocit
y);
int initialVelocity = (int) VelocityTrackerCompat.getXVeloci
ty(
velocityTracker, mActivePointerId);
mPopulatePending = true;
final int width = getClientWidth();
final int scrollX = getScrollX();
final ItemInfo ii = infoForCurrentScrollPosition();
final int currentPage = ii.position;
se();
se();
);
mActivePointerId = INVALID_POINTER;
endDrag();
needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelea
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
scrollToItem(mCurItem, true, 0, false);
mActivePointerId = INVALID_POINTER;
endDrag();
needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelea
}
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
final float x = MotionEventCompat.getX(ev, index);
mLastMotionX = x;
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionX = MotionEventCompat.getX(ev,
MotionEventCompat.findPointerIndex(ev, mActivePointerId)
break;
}
if (needsInvalidate) {
ViewCompat.postInvalidateOnAnimation(this);
}
return true;
return needsInvalidate;
/**
* @return Info about the page at the current scroll position.
*
This can be synthetic for a missing middle page; the 'object' fie
ld can be null.
*/
private ItemInfo infoForCurrentScrollPosition() {
final int width = getClientWidth();
final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
int lastPos = -1;
float lastOffset = 0.f;
float lastWidth = 0.f;
boolean first = true;
ItemInfo lastItem = null;
for (int i = 0; i < mItems.size(); i++) {
ItemInfo ii = mItems.get(i);
float offset;
if (!first && ii.position != lastPos + 1) {
// Create a synthetic item for a missing page.
ii = mTempItem;
ii.offset = lastOffset + lastWidth + marginOffset;
ii.position = lastPos + 1;
ii.widthFactor = mAdapter.getPageWidth(ii.position);
i--;
}
offset = ii.offset;
}
}
return lastItem;
return targetPage;
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
boolean needsInvalidate = false;
tom();
canvas.rotate(270);
canvas.translate(-height + getPaddingTop(), mFirstOffset * width
);
mLeftEdge.setSize(height, width);
needsInvalidate |= mLeftEdge.draw(canvas);
canvas.restoreToCount(restoreCount);
tom();
}
if (!mRightEdge.isFinished()) {
final int restoreCount = canvas.save();
final int width = getWidth();
final int height = getHeight() - getPaddingTop() - getPaddingBot
canvas.rotate(90);
canvas.translate(-getPaddingTop(), -(mLastOffset + 1) * width);
mRightEdge.setSize(height, width);
needsInvalidate |= mRightEdge.draw(canvas);
canvas.restoreToCount(restoreCount);
}
} else {
mLeftEdge.finish();
mRightEdge.finish();
}
if (needsInvalidate) {
// Keep animating
ViewCompat.postInvalidateOnAnimation(this);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw the margin drawable between pages if needed.
if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && m
Adapter != null) {
final int scrollX = getScrollX();
final int width = getWidth();
final float marginOffset = (float) mPageMargin / width;
int itemIndex = 0;
ItemInfo ii = mItems.get(0);
float offset = ii.offset;
final int itemCount = mItems.size();
final int firstPos = ii.position;
final int lastPos = mItems.get(itemCount - 1).position;
for (int pos = firstPos; pos < lastPos; pos++) {
while (pos > ii.position && itemIndex < itemCount) {
ii = mItems.get(++itemIndex);
}
float drawAt;
if (pos == ii.position) {
drawAt = (ii.offset + ii.widthFactor) * width;
offset = ii.offset + ii.widthFactor + marginOffset;
} else {
float widthFactor = mAdapter.getPageWidth(pos);
drawAt = (offset + widthFactor) * width;
ds);
mMarginDrawable.draw(canvas);
/**
* Start a fake drag of the pager.
*
* <p>A fake drag can be useful if you want to synchronize the motion of the
ViewPager
* with the touch scrolling of another view, while still letting the ViewPag
er
* control the snapping motion and fling behavior. (e.g. parallax-scrolling
tabs.)
* Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
* {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
*
* <p>During a fake drag the ViewPager will ignore all touch events. If a re
al drag
* is already in progress, this method will return false.
*
* @return true if the fake drag began successfully, false if it could not b
e started.
*
* @see #fakeDragBy(float)
* @see #endFakeDrag()
*/
public boolean beginFakeDrag() {
if (mIsBeingDragged) {
return false;
}
mFakeDragging = true;
setScrollState(SCROLL_STATE_DRAGGING);
mInitialMotionX = mLastMotionX = 0;
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
} else {
mVelocityTracker.clear();
}
final long time = SystemClock.uptimeMillis();
final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION
_DOWN, 0, 0, 0);
mVelocityTracker.addMovement(ev);
ev.recycle();
mFakeDragBeginTime = time;
return true;
}
/**
* End a fake drag of the pager.
*
* @see #beginFakeDrag()
* @see #fakeDragBy(float)
*/
public void endFakeDrag() {
if (!mFakeDragging) {
throw new IllegalStateException("No fake drag in progress. Call begi
nFakeDrag first.");
}
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
velocityTracker, mActivePointerId);
mPopulatePending = true;
final int width = getClientWidth();
final int scrollX = getScrollX();
final ItemInfo ii = infoForCurrentScrollPosition();
final int currentPage = ii.position;
final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.wi
dthFactor;
final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
int nextPage = determineTargetPage(currentPage, pageOffset, initialVeloc
ity,
totalDelta);
setCurrentItemInternal(nextPage, true, true, initialVelocity);
endDrag();
}
mFakeDragging = false;
/**
* Fake drag by an offset in pixels. You must have called {@link #beginFakeD
rag()} first.
*
* @param xOffset Offset in pixels to drag by.
* @see #beginFakeDrag()
* @see #endFakeDrag()
*/
public void fakeDragBy(float xOffset) {
if (!mFakeDragging) {
throw new IllegalStateException("No fake drag in progress. Call begi
nFakeDrag first.");
}
mLastMotionX += xOffset;
float oldScrollX = getScrollX();
float scrollX = oldScrollX - xOffset;
final int width = getClientWidth();
float leftBound = width * mFirstOffset;
float rightBound = width * mLastOffset;
final ItemInfo firstItem = mItems.get(0);
final ItemInfo lastItem = mItems.get(mItems.size() - 1);
if (firstItem.position != 0) {
leftBound = firstItem.offset * width;
}
if (lastItem.position != mAdapter.getCount() - 1) {
rightBound = lastItem.offset * width;
}
if (scrollX < leftBound) {
scrollX = leftBound;
} else if (scrollX > rightBound) {
scrollX = rightBound;
}
// Don't lose the rounded component
mLastMotionX += scrollX - (int) scrollX;
scrollTo((int) scrollX, getScrollY());
pageScrolled((int) scrollX);
// Synthesize an event for the VelocityTracker.
final long time = SystemClock.uptimeMillis();
final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, Moti
onEvent.ACTION_MOVE,
mLastMotionX, 0, 0);
mVelocityTracker.addMovement(ev);
ev.recycle();
}
/**
* Returns true if a fake drag is in progress.
*
* @return true if currently in a fake drag, false otherwise.
*
* @see #beginFakeDrag()
* @see #fakeDragBy(float)
* @see #endFakeDrag()
*/
public boolean isFakeDragging() {
return mFakeDragging;
}
x);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
/**
* Tests scrollability within child views of v given a delta of dx.
*
* @param v View to test for horizontal scrollability
* @param checkV Whether the view v passed should itself be checked for scro
llability (true),
*
or just its children (false).
* @param dx Delta scrolled in pixels
* @param x X coordinate of the active touch point
* @param y Y coordinate of the active touch point
* @return true if child views of v can be scrolled by delta of dx.
*/
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
if (v instanceof ViewGroup) {
final ViewGroup group = (ViewGroup) v;
final int scrollX = v.getScrollX();
final int scrollY = v.getScrollY();
final int count = group.getChildCount();
// Count backwards - let topmost views consume scroll distance first
.
for (int i = count - 1; i >= 0; i--) {
// TODO: Add versioned support here for transformed views.
// This will not work for transformed views in Honeycomb+
final View child = group.getChildAt(i);
if (x + scrollX >= child.getLeft() && x + scrollX < child.getRig
ht() &&
Bottom() &&
y + scrollY - child.getTop())) {
}
}
return true;
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// Let the focused view and/or our descendants get the key first
return super.dispatchKeyEvent(event) || executeKeyEvent(event);
}
/**
* You can call this function yourself to have the scroll view perform
* scrolling from a key event, just as if the event had been dispatched to
* it by the view hierarchy.
*
* @param event The key event to execute.
* @return Return true if the event was handled, else false.
*/
public boolean executeKeyEvent(KeyEvent event) {
boolean handled = false;
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
handled = arrowScroll(FOCUS_LEFT);
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
handled = arrowScroll(FOCUS_RIGHT);
break;
case KeyEvent.KEYCODE_TAB:
if (Build.VERSION.SDK_INT >= 11) {
// The focus finder had a bug handling FOCUS_FORWARD and
FOCUS_BACKWARD
// before Android 3.0. Ignore the tab key on those devic
es.
if (KeyEventCompat.hasNoModifiers(event)) {
handled = arrowScroll(FOCUS_FORWARD);
} else if (KeyEventCompat.hasModifiers(event, KeyEvent.M
ETA_SHIFT_ON)) {
handled = arrowScroll(FOCUS_BACKWARD);
}
}
break;
}
}
return handled;
}
public boolean arrowScroll(int direction) {
View currentFocused = findFocus();
if (currentFocused == this) {
currentFocused = null;
Focused,
direction);
if (nextFocused != null && nextFocused != currentFocused) {
if (direction == View.FOCUS_LEFT) {
// If there is nothing to the left, or this