使用的布局使用的是GitHub:https://github.com/Cleveroad/slidingtutorial-android
第一步:添加:compile ‘com.android.support:percent:25.3.1’
第二步:布局
- fragment_page_first
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootFirstPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="@android:color/holo_orange_dark"
tools:ignore="ContentDescription">
<ImageView
android:id="@+id/ivFirstImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/s_0_1"
app:layout_heightPercent="35%"
app:layout_widthPercent="50%"
app:translationXIn="0.4"
app:translationYIn="0.4"
app:translationYOut="0.4"
app:translationXOut="0.4" />
<ImageView
android:id="@+id/ivSecondImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:src="@mipmap/s_0_2"
app:layout_heightPercent="10%"
app:layout_marginRightPercent="12%"
app:layout_marginTopPercent="27%"
app:layout_widthPercent="12%"
app:translationXIn="0.12"
app:translationYIn="0.82"
app:translationYOut="0.82"
app:translationXOut="0.12" />
<ImageView
android:id="@+id/ivThirdImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_0_3"
app:layout_heightPercent="25%"
app:layout_marginLeftPercent="14%"
app:layout_marginTopPercent="49%"
app:layout_widthPercent="30%"
app:translationXIn="0.16"
app:translationXOut="0.16" />
<ImageView
android:id="@+id/ivFourthImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_0_4"
app:layout_heightPercent="15%"
app:layout_marginLeftPercent="14%"
app:layout_marginTopPercent="39%"
app:layout_widthPercent="20%"
app:translationXIn="0.02"
app:translationXOut="0.02" />
<ImageView
android:id="@+id/ivFifthImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@mipmap/s_0_5"
app:layout_heightPercent="15%"
app:layout_marginTopPercent="22%"
app:layout_widthPercent="45%"
app:translationXIn="0.06"
app:translationXOut="0.06" />
<ImageView
android:id="@+id/ivSixthImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_0_6"
app:layout_heightPercent="6%"
app:layout_marginLeftPercent="4%"
app:layout_marginTopPercent="26%"
app:layout_widthPercent="6%"
app:translationXIn="0.18"
app:translationXOut="0.18" />
<ImageView
android:id="@+id/ivSeventhImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_0_7"
app:layout_heightPercent="8%"
app:layout_marginLeftPercent="14%"
app:layout_marginTopPercent="25%"
app:layout_widthPercent="9%"
app:translationXIn="0.28"
app:translationXOut="0.28" />
<ImageView
android:id="@+id/ivEighthImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_0_8"
app:layout_heightPercent="6%"
app:layout_marginLeftPercent="77%"
app:layout_marginTopPercent="38%"
app:layout_widthPercent="8%"
app:translationXIn="0.34"
app:translationXOut="0.34" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="@string/text_web_ceo"
android:textColor="@android:color/white"
android:textSize="@dimen/text_size_large"
app:layout_heightPercent="15%"
app:layout_marginBottomPercent="11%"
app:layout_widthPercent="45%" />
</android.support.percent.PercentRelativeLayout>
- fragement_page_second
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout
android:id="@+id/rootSecondPage"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription"
tools:background="@android:color/holo_green_dark">
<ImageView
android:id="@+id/ivFirstImage"
app:translationXIn="0.4"
app:translationXOut="0.4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/s_1_1"
app:layout_heightPercent="25%"
app:layout_widthPercent="45%"/>
<ImageView
android:id="@+id/ivSecondImage"
app:translationXIn="1.2"
app:translationXOut="1.2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:alpha=".5"
android:src="@mipmap/s_1_2"
app:layout_heightPercent="6%"
app:layout_marginRightPercent="14%"
app:layout_marginTopPercent="30%"
app:layout_widthPercent="19%"/>
<ImageView
android:id="@+id/ivThirdImage"
app:translationXIn="0.16"
app:translationXOut="0.16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_1_3"
app:layout_heightPercent="8%"
app:layout_marginLeftPercent="46%"
app:layout_marginTopPercent="43%"
app:layout_widthPercent="15%"/>
<ImageView
android:id="@+id/ivFourthImage"
app:translationXIn="0.2"
app:translationXOut="0.2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_1_4"
app:layout_heightPercent="10%"
app:layout_marginLeftPercent="44%"
app:layout_marginTopPercent="52%"
app:layout_widthPercent="13%"/>
<ImageView
app:translationXIn="0.06"
app:translationXOut="0.06"
android:id="@+id/ivSixthImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_1_6"
app:layout_heightPercent="7%"
app:layout_marginLeftPercent="20%"
app:layout_marginTopPercent="47%"
app:layout_widthPercent="20%"/>
<ImageView
app:translationXIn="0.18"
app:translationXOut="0.18"
android:id="@+id/ivSeventhImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_1_7"
app:layout_heightPercent="8%"
app:layout_marginLeftPercent="22%"
app:layout_marginTopPercent="35%"
app:layout_widthPercent="17%"/>
<ImageView
app:translationXIn="0.28"
app:translationXOut="0.28"
android:id="@+id/ivFifthImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@mipmap/s_1_5"
app:layout_heightPercent="10%"
app:layout_marginTopPercent="33%"
app:layout_widthPercent="15%"/>
<ImageView
app:translationXIn="0.14"
app:translationXOut="0.14"
android:id="@+id/ivEighthImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@mipmap/s_1_8"
app:layout_heightPercent="10%"
app:layout_marginTopPercent="22%"
app:layout_widthPercent="25%"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="@string/text_web_dev"
android:textColor="@android:color/white"
android:textSize="@dimen/text_size_large"
app:layout_heightPercent="15%"
app:layout_marginBottomPercent="11%"
app:layout_widthPercent="45%"/>
</android.support.percent.PercentRelativeLayout>
- fragment_page_third
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout
android:id="@+id/rootThirdPage"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription"
tools:background="@android:color/holo_blue_dark">
<ImageView
android:id="@+id/ivThirdImage"
app:translationXIn="0.4"
app:translationXOut="0.4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_2_3"
app:layout_heightPercent="40%"
app:layout_marginTopPercent="27%"
app:layout_widthPercent="100%"/>
<ImageView
android:id="@+id/ivSecondImage"
app:translationXIn="0.12"
app:translationXOut="0.12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha=".5"
android:src="@mipmap/s_2_2"
app:layout_heightPercent="27%"
app:layout_marginLeftPercent="20%"
app:layout_marginRightPercent="12%"
app:layout_marginTopPercent="20%"
app:layout_widthPercent="75%"/>
<ImageView
android:id="@+id/ivFirstImage"
app:translationXIn="0.16"
app:translationXOut="0.16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@mipmap/s_2_1"
app:layout_heightPercent="18%"
app:layout_widthPercent="55%"/>
<ImageView
android:id="@+id/ivFourthImage"
app:translationXIn="0.2"
app:translationXOut="0.2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_2_4"
app:layout_heightPercent="7%"
app:layout_marginLeftPercent="33%"
app:layout_marginTopPercent="34%"
app:layout_widthPercent="16%"/>
<ImageView
android:id="@+id/ivSixthImage"
app:translationXIn="0.06"
app:translationXOut="0.06"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_2_6"
app:layout_heightPercent="7%"
app:layout_marginLeftPercent="57%"
app:layout_marginTopPercent="29%"
app:layout_widthPercent="14%"/>
<ImageView
android:id="@+id/ivSeventhImage"
app:translationXIn="0.18"
app:translationXOut="0.18"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_2_7"
app:layout_heightPercent="15%"
app:layout_marginLeftPercent="65%"
app:layout_marginTopPercent="47%"
app:layout_widthPercent="25%"/>
<ImageView
android:id="@+id/ivFifthImage"
app:translationXIn="0.28"
app:translationXOut="0.28"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/s_2_5"
app:layout_heightPercent="10%"
app:layout_marginLeftPercent="77%"
app:layout_marginTopPercent="30%"
app:layout_widthPercent="15%"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="@string/text_e_commerce"
android:textColor="@android:color/white"
android:textSize="@dimen/text_size_large"
app:layout_heightPercent="15%"
app:layout_marginBottomPercent="11%"
app:layout_widthPercent="45%"/>
</android.support.percent.PercentRelativeLayout>
- activity_main
<?xml version="1.0" encoding="utf-8"?>
<com.hbwj.a19_.animation.ParallaxViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parallax_vp"
android:layout_width="match_parent"
android:background="@android:color/holo_orange_dark"
android:layout_height="match_parent"
tools:context=".MainActivity"/>
ParallaxViewPager:自定义viewPager
public class ParallaxViewPager extends ViewPager {
private List<ParallaxFragment> mFragments;
public ParallaxViewPager(Context context) {
this(context, null);
}
public ParallaxViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
mFragments = new ArrayList<>();
}
public void setLayout(FragmentManager fm, int[] layoutIds) {
mFragments.clear();
for (int layoutId : layoutIds) {
ParallaxFragment fragment = new ParallaxFragment();
Bundle bundle = new Bundle();
bundle.putInt(fragment.LAYOUT_ID_KEY, layoutId);
fragment.setArguments(bundle);
mFragments.add(fragment);
}
// 设置我们的 ViewPager 的Adapter
setAdapter(new ParallaxPagerAdapter(fm));
//设置事件
addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// 滚动 position 当前位置 positionOffset 0-1 positionOffsetPixels 0-屏幕的宽度px
Log.e("TAG", "position->" + position + " positionOffset->" + positionOffset + " positionOffsetPixels->" + positionOffsetPixels);
//获得属性
//左边出去,右边进来
ParallaxFragment outFragment = mFragments.get(position);
List<View> parallaxViews = outFragment.getParallaxViews();
for (View parallaxView : parallaxViews) {
ParallaxTag tag = (ParallaxTag) parallaxView.getTag(R.id.parallax_tag);
parallaxView.setTranslationX(-positionOffsetPixels * tag.translationXOut);
parallaxView.setTranslationY(-positionOffsetPixels * tag.translationYOut);
}
try {
//从右边进来
ParallaxFragment inFramgent = mFragments.get(position + 1);
List<View> inParallaxViews = inFramgent.getParallaxViews();
for (View parallaxView : inParallaxViews) {
ParallaxTag tag = (ParallaxTag) parallaxView.getTag(R.id.parallax_tag);
parallaxView.setTranslationX((getMeasuredWidth() - positionOffsetPixels) * tag.translationXIn);
parallaxView.setTranslationY((getMeasuredWidth() - positionOffsetPixels) * tag.translationYIn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onPageSelected(int position) {
//页面切换完毕
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private class ParallaxPagerAdapter extends FragmentPagerAdapter {
public ParallaxPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
}
}
ParallaxFragment:View拦截解析属性
public class ParallaxFragment extends Fragment implements LayoutInflaterFactory {
public static final String LAYOUT_ID_KEY = "LAYOUT_ID_KEY";
private CompatViewInflater mAppCompatViewInflater;
private static final boolean IS_PRE_LOLLIPOP = Build.VERSION.SDK_INT < 21;
private int[] mParallaxAttrs = new int[]{R.attr.translationXIn,
R.attr.translationXOut, R.attr.translationYIn, R.attr.translationYOut};
// 存放所有的需要位移的View
private List<View> mParallaxViews = new ArrayList<>();
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
int layoutId = getArguments().getInt(LAYOUT_ID_KEY);
// View创建的时候 我们去解析属性 这里传 inflater 有没有问题?
// 单例设计模式 代表着所有的View的创建都会是该 Fragment 去创建的
inflater = inflater.cloneInContext(getContext());//克隆一个
LayoutInflaterCompat.setFactory(inflater, this);
return inflater.inflate(layoutId, container, false);
}
// View都会来这里,创建View
// 拦截到View的创建 获取View之后要去解析
// 1. 创建View
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
//View都会来这里,创建view
// First let the Activity's Factory try and inflate the view
// First let the Activity's Factory try and inflate the view
View view = createView(parent, name, context, attrs);
if (view != null) {
//解析属性
analysisAttrs(view,context,attrs);
}
// If the Factory didn't handle it, let our createView() method try
return view;
}
/**
* 解析属性
*/
private void analysisAttrs(View view, Context context, AttributeSet attrs) {
TypedArray array=context.obtainStyledAttributes(attrs,mParallaxAttrs);
if(array!=null&&array.getIndexCount()!=0){
/* float xIn =attar.getFloat(0,0f);
float xOut =attar.getFloat(1,0f);
float yIn =attar.getFloat(2,0f);
float yOut=attar.getFloat(3,0f);*/
int n = array.getIndexCount();
ParallaxTag tag=new ParallaxTag();
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
switch (attr) {
case 0:
tag.translationXIn = array.getFloat(attr,0f);
break;
case 1:
tag.translationXOut = array.getFloat(attr,0f);
break;
case 2:
tag.translationYIn = array.getFloat(attr,0f);
break;
case 3:
tag.translationYOut = array.getFloat(attr,0f);
break;
}
}
//保存自定义view
view.setTag(R.id.parallax_tag,tag);
//Log.e("TAG",tag.toString());
mParallaxViews.add(view);
}
array.recycle();
}
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
mAppCompatViewInflater = new CompatViewInflater();
}
boolean inheritContext = (attrs instanceof XmlPullParser)
// If we have a XmlPullParser, we can detect where we are in the layout
? ((XmlPullParser) attrs).getDepth() > 1
// Otherwise we have to use the old heuristic
: shouldInheritContext((ViewParent) parent);
return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
true, /* Read read app:theme as a fallback at all times for legacy reasons */
VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
);
}
private boolean shouldInheritContext(ViewParent parent) {
if (parent == null) {
// The initial parent is null so just return false
return false;
}
while (true) {
if (parent == null) {
// Bingo. We've hit a view which has a null parent before being terminated from
// the loop. This is (most probably) because it's the root view in an inflation
// call, therefore we should inherit. This works as the inflated layout is only
// added to the hierarchy at the end of the inflate() call.
return true;
} else if (!(parent instanceof View)
|| ViewCompat.isAttachedToWindow((View) parent)) {
// We have either hit the window's decor view, a parent which isn't a View
// (i.e. ViewRootImpl), or an attached view, so we know that the original parent
// is currently added to the view hierarchy. This means that it has not be
// inflated in the current inflate() call and we should not inherit the context.
return false;
}
parent = parent.getParent();
}
}
//获得所有属性
public List<View> getParallaxViews() {
return mParallaxViews;
}
}
CompatViewInflater:参考系统源码
public class CompatViewInflater {
private static final Class<?>[] sConstructorSignature = new Class[]{
Context.class, AttributeSet.class};
private static final String LOG_TAG = "AppCompatViewInflater";
private final Object[] mConstructorArgs = new Object[2];
private static final Map<String, Constructor<? extends View>> sConstructorMap
= new ArrayMap<>();
private static final int[] sOnClickAttrs = new int[]{android.R.attr.onClick};
public final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext = context;
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
// by using the parent's context
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
// We then apply the theme on the context, if specified
context = themifyContext(context, attrs, readAndroidTheme, readAppTheme);
}
if (wrapContext) {
context = TintContextWrapper.wrap(context);
}
View view = null;
// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {
case "TextView":
view = new AppCompatTextView(context, attrs);
break;
case "ImageView":
view = new AppCompatImageView(context, attrs);
break;
case "Button":
view = new AppCompatButton(context, attrs);
break;
case "EditText":
view = new AppCompatEditText(context, attrs);
break;
case "Spinner":
view = new AppCompatSpinner(context, attrs);
break;
case "ImageButton":
view = new AppCompatImageButton(context, attrs);
break;
case "CheckBox":
view = new AppCompatCheckBox(context, attrs);
break;
case "RadioButton":
view = new AppCompatRadioButton(context, attrs);
break;
case "CheckedTextView":
view = new AppCompatCheckedTextView(context, attrs);
break;
case "AutoCompleteTextView":
view = new AppCompatAutoCompleteTextView(context, attrs);
break;
case "MultiAutoCompleteTextView":
view = new AppCompatMultiAutoCompleteTextView(context, attrs);
break;
case "RatingBar":
view = new AppCompatRatingBar(context, attrs);
break;
case "SeekBar":
view = new AppCompatSeekBar(context, attrs);
break;
}
if (view == null && originalContext != context) {
// If the original context does not equal our themed context, then we need to manually
// inflate it using the name so that android:theme takes effect.
view = createViewFromTag(context, name, attrs);
}
if (view != null) {
// If we have created a view, check it's android:onClick
checkOnClickListener(view, attrs);
}
return view;
}
private static Context themifyContext(Context context, AttributeSet attrs,
boolean useAndroidTheme, boolean useAppTheme) {
final TypedArray a = context.obtainStyledAttributes(attrs, android.support.v7.appcompat.R.styleable.View, 0, 0);
int themeId = 0;
if (useAndroidTheme) {
// First try reading android:theme if enabled
themeId = a.getResourceId(android.support.v7.appcompat.R.styleable.View_android_theme, 0);
}
if (useAppTheme && themeId == 0) {
// ...if that didn't work, try reading app:theme (for legacy reasons) if enabled
themeId = a.getResourceId(android.support.v7.appcompat.R.styleable.View_theme, 0);
if (themeId != 0) {
Log.i(LOG_TAG, "app:theme is now deprecated. "
+ "Please move to using android:theme instead.");
}
}
a.recycle();
if (themeId != 0 && (!(context instanceof ContextThemeWrapper)
|| ((ContextThemeWrapper) context).getThemeResId() != themeId)) {
// If the context isn't a ContextThemeWrapper, or it is but does not have
// the same theme as we need, wrap it in a new wrapper
context = new ContextThemeWrapper(context, themeId);
}
return context;
}
private View createViewFromTag(Context context, String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
try {
mConstructorArgs[0] = context;
mConstructorArgs[1] = attrs;
if (-1 == name.indexOf('.')) {
// try the android.widget prefix first...
return createView(context, name, "android.widget.");
} else {
return createView(context, name, null);
}
} catch (Exception e) {
// We do not want to catch these, lets return null and let the actual LayoutInflater
// try
return null;
} finally {
// Don't retain references on context.
mConstructorArgs[0] = null;
mConstructorArgs[1] = null;
}
}
private void checkOnClickListener(View view, AttributeSet attrs) {
final Context context = view.getContext();
if (!(context instanceof ContextWrapper) ||
(Build.VERSION.SDK_INT >= 15 && !ViewCompat.hasOnClickListeners(view))) {
// Skip our compat functionality if: the Context isn't a ContextWrapper, or
// the view doesn't have an OnClickListener (we can only rely on this on API 15+ so
// always use our compat code on older devices)
return;
}
final TypedArray a = context.obtainStyledAttributes(attrs, sOnClickAttrs);
final String handlerName = a.getString(0);
if (handlerName != null) {
view.setOnClickListener(new DeclaredOnClickListener(view, handlerName));
}
a.recycle();
}
private View createView(Context context, String name, String prefix)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
try {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
Class<? extends View> clazz = context.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
constructor = clazz.getConstructor(sConstructorSignature);
sConstructorMap.put(name, constructor);
}
constructor.setAccessible(true);
return constructor.newInstance(mConstructorArgs);
} catch (Exception e) {
// We do not want to catch these, lets return null and let the actual LayoutInflater
// try
return null;
}
}
private static class DeclaredOnClickListener implements View.OnClickListener {
private final View mHostView;
private final String mMethodName;
private Method mResolvedMethod;
private Context mResolvedContext;
public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
mHostView = hostView;
mMethodName = methodName;
}
@Override
public void onClick(@NonNull View v) {
if (mResolvedMethod == null) {
resolveMethod(mHostView.getContext(), mMethodName);
}
try {
mResolvedMethod.invoke(mResolvedContext, v);
} catch (IllegalAccessException e) {
throw new IllegalStateException(
"Could not execute non-public method for android:onClick", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(
"Could not execute method for android:onClick", e);
}
}
@NonNull
private void resolveMethod(@Nullable Context context, @NonNull String name) {
while (context != null) {
try {
if (!context.isRestricted()) {
final Method method = context.getClass().getMethod(mMethodName, View.class);
if (method != null) {
mResolvedMethod = method;
mResolvedContext = context;
return;
}
}
} catch (NoSuchMethodException e) {
// Failed to find method, keep searching up the hierarchy.
}
if (context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
} else {
// Can't search up the hierarchy, null out and fail.
context = null;
}
}
final int id = mHostView.getId();
final String idText = id == View.NO_ID ? "" : " with id '"
+ mHostView.getContext().getResources().getResourceEntryName(id) + "'";
throw new IllegalStateException("Could not find method " + mMethodName
+ "(View) in a parent or ancestor Context for android:onClick "
+ "attribute defined on view " + mHostView.getClass() + idText);
}
}
}
ParallaxTag:
public class ParallaxTag {
public float translationXIn;
public float translationXOut;
public float translationYIn;
public float translationYOut;
@Override
public String toString() {
return "translationXIn->"+translationXIn+" translationXOut->"+translationXOut
+" translationYIn->"+translationYIn+" translationYOut->"+translationYOut;
}
}
自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- X方向上的位移 -->
<attr name="translationXIn" format="float" />
<attr name="translationXOut" format="float" />
<!-- Y方向上的位移 -->
<attr name="translationYIn" format="float" />
<attr name="translationYOut" format="float" />
</resources>