高仿有赞微小店SplashView
2015-09-18 16:08
441 查看
从事android开发有段时间了,总是说该写写技术博客了,分享知识的同时也能从中知道自己的不足,有挫折才会有动力,有争吵才会有成长,大家一起共勉吧。
直接进入主题吧,前两天正好看见一款App,对里面的Splash View给吸引住了, 像我们一般的App一般不会在Splash View花费太多的时间折腾,小公司大都功能至上,体验次之。从一个App的每个设计细节往往能够窥探出公司对产品和客户的责任心,当然了,这个有点扯远了,整个功能倒不是很难,我们一起来分析分析吧,么么哒!
首先我们从平台上下载有赞微小店,将.apk后缀改成.zip解压到任意文件夹,文件夹内容如下:
![](http://img.blog.csdn.net/20150918131512222)
在res里面会找到相应的资源,往往大点的公司美工都是很不错的,图片也切的比较精美。
拿到想要的资源后,来分析分析设计需求吧:
1:每个子图片都是相对性的布局在自己的位子,并且以动画的形式以一定的时间间隔进行展示,凸显层次感。
2:滑动的过程中,后面的背景图不会跟着一起滑动,滑动的过程中背景图的透明度会随着页面的左右滑动正比渐变,直至完整滑出页面。
3:每滑到新页面都会将旧页面进行隐藏,并以动画的形式展现新页面,凸显层次感。
大概就这么多了,运行效果图如下,加深大家的理解:
![](http://img.blog.csdn.net/20150921094832151)
下面我们一起用代码来实现:
主布局代码activity_main.xml:
首先讲一下这个布局整体思路吧,也不难就是麻烦!–(,父类布局是FrameLayout,用这个的原因是最外层会遮罩一个InitSplashView图片,InitSplashView大概会显示2秒后将其状态设为INVISIABLE,然后显示的就是SplashView的,这个也是比较简单的做法了。当我们滑动ViewPager的时候ViewPager后面的背景是不会横着一起滑动的,所以后面的背景图肯定不是画在ViewPager所托管的子页面上,我们设置ViewPager的子页面背景为透明,这样后面的布局我们是可以看见的而且不会跟着ViewPager一起滑动,至于滑动过程后面的背景会跟着滑动偏移比例透明度会正比渐变,这个我们下面一起操作起来,很好玩的。
这边我们会有三个子页面,子页面里面的小图片都是以属性动画的形式进行展示的,当然我这边偷懒 ,minSdkVersion=11,哈哈,如果要兼容到3.0以下的话,还是乖乖的引入nineOldAndroids吧。
首先实现一个接口:
接口两个方法分别为:startAnimator(启动动画), 2:withChangeAnimator(随着ViewPager的滑动正比渐变背景);
每个页面的动画单独抽成子类进行维护,这样可以减少代码耦合,也能提高代码的可维护度。
FirstAnimator:
SecondAnimator.ThirdAnimator与上面显示雷同:
首先startAimator方法的触发时机为两种:
1:InitSplashView过渡两秒之后触发:
2:第二种触发方法完全依赖OnPageChangeListener的实现监听:
那我们来看下updateIndicatorStatus到底是干什么的:
这里我们会根据滑动监听动态改变Indicator Icon的背景图,同时我们会触发相应页面的初始动画,这里动画的统一管理我们会放在AnimatorManager进行管理:
代码如下:
在startAnimtor里面我们会隐藏其他页面,显示当前页面。这里我们通过简单工厂得到动画操作实例:
得到相应实例时候会相应的通过多态调用子类中StartAimator方法,顾客只需要调用某个方法,具体的逻辑子类自己去实现,顾客与用户进行解耦。
我们再来看下如何让ViewPager的子页面跟随滑动正比渐变,这个我们就要去了解OnPageChangeListener这个接口实现方法的具体含义了:
1:public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
——-时刻监听ViewPager的滑动状态,positionOffset为滑动的比例0f-1f,positionOffsetPixels为当前页面偏移的像素位置 。
2: public void onPageSelected(int position)
——-滑动完成之后掉用。
3: public void onPageScrollStateChanged(int state)
——-监听滑动状态:state==1时默示正在滑动,state==2时默示滑动完毕了,state==0时默示什么都没做。
然后思路就来了,我们根据positionOffset来动态设置整个子布局的ahpla(透明度),这样就可以达到效果了,开始动手吧。
代码如下:
在FirstAnimator里面自己实现:
这里面实现的过程中遇到一些问题,ViewPager页面滑动过程中PositionOffset渐变比例会随着滑动方向而不同,向左滑动比例会从1f-0f, 向右滑动比例会从0f-1f, 但是向左滑动时,position会是上个页面的position,不调试不知道,调试完了之后吓一跳,没办法,凡事都得实践才能出真理,所以这边做了特殊处理,还有就是当positionOffsetPix == 0时,我们会设置alpha为不透明,这样能保证当滑动到一半时松手自然回弹到当前页面布局透明度为0;
主MainActivity:
再有就是动画方面了,无非就是对属性的一些操作;
具体涉及到这些:
1:透明度,X,Y轴的缩放同步播放:
2:箭头显示的动画:
等等,有动画基础的同学这些都是小儿科了。
还有一个地方比较重要:
就是启动子页面动画的时候,不会去触发ViewPager滑动引起的透明度改变,这样会避免闪烁现象。
这里主要是监听启动动画的回调:
当然我们在AnimatorManger会实现一个接口:
然后将接口注册到FirstAnimator中,通过监听动画的执行状态通过callback接口回调给AnimatorManager;
我们在启动withChangeAnimator的时候会去判断回调状态:
每个页面的动画滑动状态会通过SparseArray进行维护:
尼玛,写博客比写代码累多了,终于倒腾倒腾的差不多了,由于第一次写技术博客,有很多小瑕疵,当然由于代码只是自己闲暇之余写的Demo,性能方面还是有待优化的,希望自己坚持下去,做一个懂得记录的Coder。
Github下载地址:https://github.com/dashentao1989/SplashView.git
直接进入主题吧,前两天正好看见一款App,对里面的Splash View给吸引住了, 像我们一般的App一般不会在Splash View花费太多的时间折腾,小公司大都功能至上,体验次之。从一个App的每个设计细节往往能够窥探出公司对产品和客户的责任心,当然了,这个有点扯远了,整个功能倒不是很难,我们一起来分析分析吧,么么哒!
首先我们从平台上下载有赞微小店,将.apk后缀改成.zip解压到任意文件夹,文件夹内容如下:
在res里面会找到相应的资源,往往大点的公司美工都是很不错的,图片也切的比较精美。
拿到想要的资源后,来分析分析设计需求吧:
1:每个子图片都是相对性的布局在自己的位子,并且以动画的形式以一定的时间间隔进行展示,凸显层次感。
2:滑动的过程中,后面的背景图不会跟着一起滑动,滑动的过程中背景图的透明度会随着页面的左右滑动正比渐变,直至完整滑出页面。
3:每滑到新页面都会将旧页面进行隐藏,并以动画的形式展现新页面,凸显层次感。
大概就这么多了,运行效果图如下,加深大家的理解:
下面我们一起用代码来实现:
主布局代码activity_main.xml:
[code]<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <RelativeLayout android:id="@+id/relativelayout_first" android:layout_width="300dp" android:layout_height="300dp" android:layout_centerInParent="true"> <ImageView android:id="@+id/start1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_marginTop="20dp" android:src="@mipmap/img_guide_page_1_star" android:visibility="gone" /> <ImageView android:id="@+id/start2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="45dp" android:layout_marginTop="3dp" android:src="@mipmap/img_guide_page_1_star" android:visibility="gone" /> <ImageView android:id="@+id/start3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="25dp" android:layout_marginRight="35dp" android:src="@mipmap/img_guide_page_1_star" android:visibility="gone" /> <ImageView android:id="@+id/first_page_img" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/img_guide_page_1" /> </RelativeLayout> <RelativeLayout android:id="@+id/relativelayout_second" android:layout_width="300dp" android:layout_height="300dp" android:layout_centerInParent="true" android:background="@mipmap/img_guide_page_bg"> <ImageView android:id="@+id/second_bg1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:src="@mipmap/img_guide_page_3_icon1" /> <ImageView android:id="@+id/second_bg4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="50dp" android:src="@mipmap/img_guide_page_3_icon4" /> <ImageView android:id="@+id/second_bg2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="40dp" android:layout_marginTop="80dp" android:src="@mipmap/img_guide_page_3_icon2" /> <ImageView android:id="@+id/second_bg3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="80dp" android:layout_marginRight="40dp" android:src="@mipmap/img_guide_page_3_icon3" /> <ImageView android:id="@+id/second_bg5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="80dp" android:layout_marginLeft="40dp" android:src="@mipmap/img_guide_page_3_icon5" /> <ImageView android:id="@+id/second_bg6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="40dp" android:layout_marginTop="80dp" android:src="@mipmap/img_guide_page_3_icon6" /> <ImageView android:id="@+id/second_bg7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@mipmap/img_guide_page_3_icon7" /> </RelativeLayout> <RelativeLayout android:id="@+id/relativelayout_third" android:layout_width="300dp" android:layout_height="300dp" android:layout_centerInParent="true" android:background="@mipmap/img_guide_page_bg"> <ImageView android:id="@+id/third_box" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="30dp" android:src="@mipmap/img_guide_page_4_box" /> <ImageView android:id="@+id/third_cash" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:paddingBottom="40dp" android:src="@mipmap/img_guide_page_4_cash" /> <ImageView android:id="@+id/third_arrow_2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="80dp" android:src="@mipmap/img_guide_page_4_arrow_2" /> <ImageView android:id="@+id/third_arrow_1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="90dp" android:src="@mipmap/img_guide_page_4_arrow_1" /> <ImageView android:id="@+id/third_arrow_3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="90dp" android:src="@mipmap/img_guide_page_4_arrow_3" /> <ImageView android:id="@+id/third_order" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginLeft="15dp" android:paddingBottom="40dp" android:src="@mipmap/img_guide_page_4_order" /> </RelativeLayout> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent"></android.support.v4.view.ViewPager> <LinearLayout android:id="@+id/linearlayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:gravity="center" android:orientation="horizontal" android:paddingBottom="10dp"> </LinearLayout> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal"> <Button android:id="@+id/login" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@drawable/login_bg" android:text="@string/login" android:textColor="#f94041" android:textSize="16sp" /> <Button android:id="@+id/register" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_weight="1" android:background="@drawable/register_bg" android:text="@string/register" android:textColor="@android:color/white" android:textSize="16sp" /> </LinearLayout> </LinearLayout> <FrameLayout android:id="@+id/splash_bg" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white" android:src="@mipmap/splash_shake" /> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="10dp" android:src="@mipmap/splash_brand" /> </FrameLayout> </FrameLayout>
首先讲一下这个布局整体思路吧,也不难就是麻烦!–(,父类布局是FrameLayout,用这个的原因是最外层会遮罩一个InitSplashView图片,InitSplashView大概会显示2秒后将其状态设为INVISIABLE,然后显示的就是SplashView的,这个也是比较简单的做法了。当我们滑动ViewPager的时候ViewPager后面的背景是不会横着一起滑动的,所以后面的背景图肯定不是画在ViewPager所托管的子页面上,我们设置ViewPager的子页面背景为透明,这样后面的布局我们是可以看见的而且不会跟着ViewPager一起滑动,至于滑动过程后面的背景会跟着滑动偏移比例透明度会正比渐变,这个我们下面一起操作起来,很好玩的。
这边我们会有三个子页面,子页面里面的小图片都是以属性动画的形式进行展示的,当然我这边偷懒 ,minSdkVersion=11,哈哈,如果要兼容到3.0以下的话,还是乖乖的引入nineOldAndroids吧。
首先实现一个接口:
[code]/** * Created by Administrator on 2015/9/17. * 动画公用接口<br/> */ public interface DefaultAnimator { void startAnimator(View view, AnimatorCallback animatorCallback); void withChangeAnimator(int position, float positionOffset, List<View> view, float positionOffsetPix, boolean flag); }
接口两个方法分别为:startAnimator(启动动画), 2:withChangeAnimator(随着ViewPager的滑动正比渐变背景);
每个页面的动画单独抽成子类进行维护,这样可以减少代码耦合,也能提高代码的可维护度。
FirstAnimator:
[code]package com.kakasure.splashdemo.animator; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.widget.ImageView; import com.kakasure.splashdemo.R; import com.kakasure.splashdemo.callback.AnimatorCallback; import java.util.List; /** * Created by Administrator on 2015/9/17. */ public class FirstAnimator implements DefaultAnimator { public View baseView; private ImageView start1; private ImageView start2; private ImageView start3; private void initView() { start1 = (ImageView) baseView.findViewById(R.id.start1); start2 = (ImageView) baseView.findViewById(R.id.start2); start3 = (ImageView) baseView.findViewById(R.id.start3); start1.setVisibility(View.GONE); start2.setVisibility(View.GONE); start3.setVisibility(View.GONE); } @Override public void startAnimator(final View view, final AnimatorCallback animatorCallback) { if (view != null) { baseView = view; initView(); ImageView imageView = (ImageView) view.findViewById(R.id.first_page_img); AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f); ObjectAnimator scalexAnimator = ObjectAnimator.ofFloat(imageView, "scaleX", 0.4f, 1f); ObjectAnimator scaleyAnimator = ObjectAnimator.ofFloat(imageView, "scaleY", 0.4f, 1f); animatorSet.play(alphaAnimator).with(scalexAnimator).with(scaleyAnimator); animatorSet.setDuration(1 * 500); animatorSet.setInterpolator(new AccelerateInterpolator()); animatorSet.start(); animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { animatorCallback.AnimatorStart(0); } @Override public void onAnimationEnd(Animator animation) { animatorCallback.AnimatorComplete(0); startAnimatorSet(start1); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } } private void startAnimatorSet(final View view) { ObjectAnimator startAnimatorX1 = ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f, 1.8f, 1f, 0.7f, 1.4f, 1.0f, 0.8f, 1f); ObjectAnimator startAnimatorY1 = ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f, 1.8f, 1f, 0.7f, 1.4f, 1.0f, 0.8f, 1f); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(200); animatorSet.setInterpolator(new AccelerateInterpolator()); animatorSet.play(startAnimatorX1).with(startAnimatorY1); animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { view.setVisibility(View.VISIBLE); } @Override public void onAnimationEnd(Animator animation) { switch (view.getId()) { case R.id.start1: startAnimatorSet(start2); break; case R.id.start2: startAnimatorSet(start3); break; case R.id.start3: // TODO NOTHING break; default: break; } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animatorSet.start(); } @Override public void withChangeAnimator(int position, float positionOffset, List<View> view, float positionOffsetPix, boolean flag) { if (view != null) { if (positionOffsetPix == 0) { view.get(position).setAlpha(1f); } else { if (flag) { view.get(position).setAlpha(1 - positionOffset); } else { view.get(position).setAlpha(positionOffset); } } } } }
SecondAnimator.ThirdAnimator与上面显示雷同:
首先startAimator方法的触发时机为两种:
1:InitSplashView过渡两秒之后触发:
[code] myHandler.sendEmptyMessageDelayed(1, 2 * 1000);
[code] private Handler myHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); splashbg.setVisibility(View.GONE); updateIndicatorStatus(0); } };
2:第二种触发方法完全依赖OnPageChangeListener的实现监听:
[code]@Override public void onPageSelected(int position) { updateIndicatorStatus(position); Log.i(TAG, "onPageSelected is called, position = " + position); }
那我们来看下updateIndicatorStatus到底是干什么的:
[code]public void updateIndicatorStatus(int position) { currentPosition = position; AnimatorManager.getInstance().startAnimator(position); switch (position) { case 0: imgList.get(0).setBackgroundResource(R.mipmap.yindao_down); imgList.get(1).setBackgroundResource(R.mipmap.yindao_on); imgList.get(2).setBackgroundResource(R.mipmap.yindao_on); break; case 1: imgList.get(1).setBackgroundResource(R.mipmap.yindao_down); imgList.get(0).setBackgroundResource(R.mipmap.yindao_on); imgList.get(2).setBackgroundResource(R.mipmap.yindao_on); break; case 2: imgList.get(2).setBackgroundResource(R.mipmap.yindao_down); imgList.get(1).setBackgroundResource(R.mipmap.yindao_on); imgList.get(0).setBackgroundResource(R.mipmap.yindao_on); break; default: break; } }
这里我们会根据滑动监听动态改变Indicator Icon的背景图,同时我们会触发相应页面的初始动画,这里动画的统一管理我们会放在AnimatorManager进行管理:
代码如下:
[code]package com.kakasure.splashdemo.manager; import android.util.SparseArray; import android.view.View; import com.kakasure.splashdemo.callback.AnimatorCallback; import com.kakasure.splashdemo.animator.AnimatorFactory; import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 2015/9/17. * 动画管理类<br/> * * @author dashentao * @date 2015 9-17 * @since V 1.0 */ public class AnimatorManager implements AnimatorCallback { // 管理View的集合 private List<View> viewList = new ArrayList<View>(); private static AnimatorManager mAnimatorManager; private static Object object = new Object(); private SparseArray<Boolean> sparseArray = new SparseArray<Boolean>(); public static AnimatorManager getInstance() { if (mAnimatorManager == null) { synchronized (object) { if (mAnimatorManager == null) { mAnimatorManager = new AnimatorManager(); } } } return mAnimatorManager; } public void add(View view) { if (viewList != null) { viewList.add(view); } } public void addAll(List<View> view) { if (viewList != null) { viewList.clear(); viewList.addAll(view); } } /** * ViewPager滑动式渐变动画<br/> * * @param position * @param positionOffset */ public void withChangeAnimator(int position, float positionOffset, float positionOffsetPix, boolean flag) { if (sparseArray != null) { if (sparseArray.get(position) != null && sparseArray.get(position)) { AnimatorFactory.getInstance(position).withChangeAnimator(position, positionOffset, viewList, positionOffsetPix, flag); } } } /** * 启动相应子页面的动画<br/> * * @param position */ public void startAnimator(int position) { // 对动画做隐藏处理 if (viewList != null && viewList.size() > 0) { for (int i = 0; i < viewList.size(); i++) { if (i == position) { viewList.get(i).setVisibility(View.VISIBLE); } else { viewList.get(i).setVisibility(View.INVISIBLE); } } } AnimatorFactory.getInstance(position).startAnimator(viewList.get(position), this); } @Override public void AnimatorComplete(int position) { if (sparseArray != null) { sparseArray.put(position, true); } } @Override public void AnimatorStart(int position) { if (sparseArray != null) { sparseArray.put(position, false); } } }
在startAnimtor里面我们会隐藏其他页面,显示当前页面。这里我们通过简单工厂得到动画操作实例:
[code]package com.kakasure.splashdemo.animator; import android.util.Log; /** * Created by Administrator on 2015/9/17. * 简单工厂类<br/> */ public class AnimatorFactory { public static final String TAG = AnimatorFactory.class.getSimpleName(); public static DefaultAnimator getInstance(int position) { switch (position) { case 0: return new FirstAnimator(); case 1: return new SecondAnimator(); case 2: return new ThirdAnimator(); default: Log.i(TAG, "you know, it will not be happened!"); break; } return null; } }
得到相应实例时候会相应的通过多态调用子类中StartAimator方法,顾客只需要调用某个方法,具体的逻辑子类自己去实现,顾客与用户进行解耦。
我们再来看下如何让ViewPager的子页面跟随滑动正比渐变,这个我们就要去了解OnPageChangeListener这个接口实现方法的具体含义了:
1:public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
——-时刻监听ViewPager的滑动状态,positionOffset为滑动的比例0f-1f,positionOffsetPixels为当前页面偏移的像素位置 。
2: public void onPageSelected(int position)
——-滑动完成之后掉用。
3: public void onPageScrollStateChanged(int state)
——-监听滑动状态:state==1时默示正在滑动,state==2时默示滑动完毕了,state==0时默示什么都没做。
然后思路就来了,我们根据positionOffset来动态设置整个子布局的ahpla(透明度),这样就可以达到效果了,开始动手吧。
代码如下:
[code]@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { Log.i(TAG, "position = " + position); boolean flag = false; if (position == currentPosition) { flag = true; } else { flag = false; } AnimatorManager.getInstance().withChangeAnimator(currentPosition, positionOffset, positionOffsetPixels, flag); }
在FirstAnimator里面自己实现:
[code]@Override public void withChangeAnimator(int position, float positionOffset, List<View> view, float positionOffsetPix, boolean flag) { if (view != null) { if (positionOffsetPix == 0) { view.get(position).setAlpha(1f); } else { if (flag) { view.get(position).setAlpha(1 - positionOffset); } else { view.get(position).setAlpha(positionOffset); } } } }
这里面实现的过程中遇到一些问题,ViewPager页面滑动过程中PositionOffset渐变比例会随着滑动方向而不同,向左滑动比例会从1f-0f, 向右滑动比例会从0f-1f, 但是向左滑动时,position会是上个页面的position,不调试不知道,调试完了之后吓一跳,没办法,凡事都得实践才能出真理,所以这边做了特殊处理,还有就是当positionOffsetPix == 0时,我们会设置alpha为不透明,这样能保证当滑动到一半时松手自然回弹到当前页面布局透明度为0;
主MainActivity:
[code]package com.kakasure.splashdemo.ui; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.kakasure.splashdemo.R; import com.kakasure.splashdemo.manager.ActManager; import com.kakasure.splashdemo.manager.AnimatorManager; import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 2015/9/16. * * @author dashentao * @date 2015 9-16 * @since V 1.0 */ public class MainActivity extends AppCompatActivity implements View.OnClickListener { private String TAG = MainActivity.this.getClass().getSimpleName(); private ViewPager viewPager; private Button login; private Button register; private LinearLayout linearLayout; private RelativeLayout relativeLayoutFirst; private RelativeLayout relativeLayoutSecond; private RelativeLayout relativeLayoutThrid; private FrameLayout splashbg; private List<ImageView> imgList = new ArrayList<ImageView>(); private List<Fragment> fragList = new ArrayList<Fragment>(); private int currentPosition = -1; private Handler myHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); splashbg.setVisibility(View.GONE); updateIndicatorStatus(0); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ActManager.getInstance().pushActivity(this); initView(); initListener(); initAdapter(); init(); } public void initListener() { login.setOnClickListener(this); register.setOnClickListener(this); viewPager.setOnPageChangeListener(new MyPageChangeListener()); } public void initView() { viewPager = (ViewPager) findViewById(R.id.viewpager); login = (Button) findViewById(R.id.login); register = (Button) findViewById(R.id.register); linearLayout = (LinearLayout) findViewById(R.id.linearlayout); relativeLayoutFirst = (RelativeLayout) findViewById(R.id.relativelayout_first); relativeLayoutSecond = (RelativeLayout) findViewById(R.id.relativelayout_second); relativeLayoutThrid = (RelativeLayout) findViewById(R.id.relativelayout_third); splashbg = (FrameLayout) findViewById(R.id.splash_bg); } /** * 初始化适配器<br/> */ public void initAdapter() { FragmentManager fragmentManager = getSupportFragmentManager(); MyAdapter myAdapter = new MyAdapter(fragmentManager); for (int i = 0; i < 3; i++) { SplashFragment splashFragment = SplashFragment.newInstance(getResources().getStringArray(R.array.titles)[i]); fragList.add(splashFragment); } myAdapter.list.clear(); myAdapter.list.addAll(fragList); viewPager.setAdapter(myAdapter); } public void init() { for (int i = 0; i < fragList.size(); i++) { ImageView imageView = new ImageView(this); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); linearLayout.addView(imageView, layoutParams); imgList.add(imageView); } List<View> view = new ArrayList<View>(); view.add(relativeLayoutFirst); view.add(relativeLayoutSecond); view.add(relativeLayoutThrid); AnimatorManager.getInstance().addAll(view); myHandler.sendEmptyMessageDelayed(1, 2 * 1000); } public void updateIndicatorStatus(int position) { currentPosition = position; AnimatorManager.getInstance().startAnimator(position); switch (position) { case 0: imgList.get(0).setBackgroundResource(R.mipmap.yindao_down); imgList.get(1).setBackgroundResource(R.mipmap.yindao_on); imgList.get(2).setBackgroundResource(R.mipmap.yindao_on); break; case 1: imgList.get(1).setBackgroundResource(R.mipmap.yindao_down); imgList.get(0).setBackgroundResource(R.mipmap.yindao_on); imgList.get(2).setBackgroundResource(R.mipmap.yindao_on); break; case 2: imgList.get(2).setBackgroundResource(R.mipmap.yindao_down); imgList.get(1).setBackgroundResource(R.mipmap.yindao_on); imgList.get(0).setBackgroundResource(R.mipmap.yindao_on); break; default: break; } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.login: // 登录状态 break; case R.id.register: // 用户注册 break; default: Log.i(TAG, "It will not happened!"); break; } } /** * 滑动页面监视器 */ public class MyPageChangeListener implements ViewPager.OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // Log.i(TAG, "positionOffset is =" + positionOffset); // Log.i(TAG, "positionOffsetPixels is = " + positionOffsetPixels); Log.i(TAG, "position = " + position); boolean flag = false; if (position == currentPosition) { flag = true; } else { flag = false; } AnimatorManager.getInstance().withChangeAnimator(currentPosition, positionOffset, positionOffsetPixels, flag); } @Override public void onPageSelected(int position) { updateIndicatorStatus(position); Log.i(TAG, "onPageSelected is called, position = " + position); } @Override public void onPageScrollStateChanged(int state) { } } /** * ViewPager适配器 */ public class MyAdapter extends FragmentPagerAdapter { List<Fragment> list = new ArrayList<Fragment>(); public MyAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return list.get(position); } @Override public int getCount() { return list.size(); } } }
再有就是动画方面了,无非就是对属性的一些操作;
具体涉及到这些:
1:透明度,X,Y轴的缩放同步播放:
[code]AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f); ObjectAnimator scalexAnimator = ObjectAnimator.ofFloat(imageView, "scaleX", 0.4f, 1f); ObjectAnimator scaleyAnimator = ObjectAnimator.ofFloat(imageView, "scaleY", 0.4f, 1f); animatorSet.play(alphaAnimator).with(scalexAnimator).with(scaleyAnimator); animatorSet.setDuration(1 * 500); animatorSet.setInterpolator(new AccelerateInterpolator()); animatorSet.start();
2:箭头显示的动画:
[code] float currentX3 = view.getTranslationX(); float currentY3 = view.getTranslationY(); ObjectAnimator arrowX3 = ObjectAnimator.ofFloat(view, "translationX", currentX3 - AppUtils.dip2px(ActManager.getInstance().getCurrentActivity(), 8), currentX3); ObjectAnimator arrowY3 = ObjectAnimator.ofFloat(view, "translationY", currentY3 + AppUtils.dip2px(ActManager.getInstance().getCurrentActivity(), 8), currentY3); animatorSet.play(arrowX3).with(arrowY3);
等等,有动画基础的同学这些都是小儿科了。
还有一个地方比较重要:
就是启动子页面动画的时候,不会去触发ViewPager滑动引起的透明度改变,这样会避免闪烁现象。
这里主要是监听启动动画的回调:
当然我们在AnimatorManger会实现一个接口:
[code]package com.kakasure.splashdemo.callback; /** * Created by Administrator on 2015/9/17. */ public interface AnimatorCallback { void AnimatorComplete(int position); void AnimatorStart(int position); }
然后将接口注册到FirstAnimator中,通过监听动画的执行状态通过callback接口回调给AnimatorManager;
[code]animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { animatorCallback.AnimatorStart(0); } @Override public void onAnimationEnd(Animator animation) { animatorCallback.AnimatorComplete(0); startAnimatorSet(start1); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } });
我们在启动withChangeAnimator的时候会去判断回调状态:
[code] /** * ViewPager滑动式渐变动画<br/> * * @param position * @param positionOffset */ public void withChangeAnimator(int position, float positionOffset, float positionOffsetPix, boolean flag) { if (sparseArray != null) { if (sparseArray.get(position) != null && sparseArray.get(position)) { AnimatorFactory.getInstance(position).withChangeAnimator(position, positionOffset, viewList, positionOffsetPix, flag); } } }
每个页面的动画滑动状态会通过SparseArray进行维护:
[code] private SparseArray<Boolean> sparseArray = new SparseArray<Boolean>();
尼玛,写博客比写代码累多了,终于倒腾倒腾的差不多了,由于第一次写技术博客,有很多小瑕疵,当然由于代码只是自己闲暇之余写的Demo,性能方面还是有待优化的,希望自己坚持下去,做一个懂得记录的Coder。
Github下载地址:https://github.com/dashentao1989/SplashView.git
相关文章推荐
- android学习笔记8 - xml布局简记
- SEAndroid中定义的客体类有哪些?(access_vectors)
- Linux dos2unix命令——转换dos文件格式成unix文件格式,unix2dos——转换unix下文件格式成dos下文件格式
- JAVA设计模式之工厂模式 简单讲解(一)
- CentOS安装和设置MariaDB的教程
- Java SocketChannel 与 SocketChannel通信模板
- 计算机专业中经典书籍(程序猿和大学生必读)
- Android学习——上下文菜单ContextMenu
- Git(Mac OSX下)
- 代码分享h5-sessionStorage,提示app下载代码块
- 【转】bootstrap 的 affix.js 插件
- tornado开发学习之1.HelloWorld和它加强版版本
- GIT和SVN比较
- iOS 9 适配系列-理解Bitcode:一种中间代码
- 简单的访问计数器
- vsphere5.5升级到6.0操作手册
- ifstat和iftop网络流量实时监控、vnstat网络流量统计,系统centos7
- 凯越obd接口,“它”如此“懂”你
- 安卓调用webservice出现的一些问题
- 端口大全及查看方法