Android UI 启动页平行动画
应用启动页面实现类似小红书平行动画功能
实现思路
- 采用ViewPager实现滑动复用引导Fragment
// 展示引导Activity
public class GuideAct extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.guide_act);
ParallaxContainer container = findViewById(R.id.parallax_container);
// 主要准备一些引导Fragment页面,传递给ParallaxContainer,设置为ViewPager子Fragment布局
container.setUp(new int[]{
R.layout.view_intro_1,
R.layout.view_intro_2,
......
});
}
}
- 看下引导 guide_act.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cn.cutcopy.anim.ParallaxContainer
......>
</cn.cutcopy.anim.ParallaxContainer>
</RelativeLayout>
单个Fragment上引导介绍元素 view_intro_1.xml,其他引导页类似
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
...... >
<!-- 自定义属性,可以在自定义LayoutInflater中获取 -->
<ImageView
android:id="@+id/iv_0"
android:layout_width="103dp"
......
app:x_in="1.2"
app:x_out="1.2" />
</RelativeLayout>
- ParallaxContainer.java (添加ViewPager实现左右滑动引导)
public class ParallaxContainer extends FrameLayout implements ViewPager.OnPageChangeListener {
private static final String TAG = "ParallaxContainer";
private List<ParallaxFragment> fragments;
private ParallaxPagerAdapter adapter;
......
public void setUp(int... childIds) {
// Fragment数组
fragments = new ArrayList<>();
for (int i = 0; i < childIds.length; i++) {
ParallaxFragment f = new ParallaxFragment();
Bundle args = new Bundle();
// Fragment中需要加载的布局文件id
args.putInt("layoutId", childIds[i]);
f.setArguments(args);
fragments.add(f);
}
ViewPager vp = new ViewPager(getContext());
vp.setId(R.id.parallax_pager);
//实例化适配器
GuideAct activity = (GuideAct) getContext();
adapter = new ParallaxPagerAdapter(activity.getSupportFragmentManager(), fragments);
......
vp.setOnPageChangeListener(this);
addView(vp, 0);
}
// 滑动引导页面,这个方法在滑动过程中会一直调用,
// 此时就可以通过这里positionOffsetPixels偏移值,另外配合每个Fragment上各个View在xml中app:x_in="1.2" 设置的偏移系数,开始执行Fragment上元素位移动画
// 每个Fragment上元素通过 ==自定义属性== 配合 [动画辅助库](https://github.com/JakeWharton/NineOldAndroids)达到动画偏移效果
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.i(TAG, "onPageScrolled position : " + position);
Log.i(TAG, "onPageScrolled positionOffset : " + positionOffset);
Log.i(TAG, "onPageScrolled positionOffsetPixels : " + positionOffsetPixels);
int containerWidth = getWidth();
ParallaxFragment outFragment = null;
try {
outFragment = fragments.get(position - 1);
} catch (Exception e) {}
//获取到退出的页面
ParallaxFragment inFragment = null;
try {
inFragment = fragments.get(position);
} catch (Exception e) {}
......
}
@Override
public void onPageSelected(int position) {
Log.i(TAG, "onPageSelected position : " + position);
......
}
@Override
public void onPageScrollStateChanged(int state) {
Log.i(TAG, "onPageScrollStateChanged state : " + state);
......
// 执行开始或结束动画
}
}
- 单个引导Fragment
// 视差Fragment
public class ParallaxFragment extends Fragment {
private List<View> parallaxViews = new ArrayList<>();
@Override
public View onCreateView(LayoutInflater original, ViewGroup container,
Bundle savedInstanceState) {
Bundle args = getArguments();
int layoutId = args.getInt("layoutId");
// 通过自定义LayoutInflater返回给Fragment布局
ParallaxLayoutInflater inflater = new ParallaxLayoutInflater(original, getActivity(),this);
return inflater.inflate(layoutId, null);
}
public List<View> getParallaxViews() {
return parallaxViews;
}
}
- 主要是通过 自定义LayoutInflater,获取到Fragment上系统或自定义View的滑动系数进行动画偏移
自定义LayoutInflater
public class ParallaxLayoutInflater extends LayoutInflater {
private ParallaxFragment fragment;
protected ParallaxLayoutInflater(Context context) {
super(context);
}
protected ParallaxLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
}
protected ParallaxLayoutInflater(LayoutInflater original, Context newContext, ParallaxFragment fragment) {
super(original, newContext);
this.fragment = fragment;
setFactory2(new ParallaxFactory(this));
}
@Override
public LayoutInflater cloneInContext(Context context) {
return new ParallaxLayoutInflater(this, context, fragment);
}
// 自定义一个加载View 的Factory
class ParallaxFactory implements Factory2 {
private LayoutInflater inflater;
public ParallaxFactory(LayoutInflater inflater) {
this.inflater = inflater;
}
// 在这里可以获取到view_intro_1.xml 中所有View设置的自定义或非自定义属性值
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attributeSet) {
View view;
......
return view;
}
}
}