Welcome

首页 / 移动开发 / Android / Android仿即刻首页垂直滚动图,炫酷到底!

项目地址:https://github.com/JeasonWong/JikeGallery
话不多说,先上效果。


这个效果是在即刻app上看到,觉得很不错,遂仿之。
先说下我的实现思路(以上方的图片滚动为例,下方的文字实现效果类似):
自定义ViewGroup
装载两个ImageView和一个阴影View
通过一定规律交替控制两个ImageView和它们的marginTop,在onLayout()中实现
marginTop的具体值由属性动画控制,不断调用requestLayout()

接下来依次说明
一、自定义ViewGroup

 //滑动状态 protected static final int STATUS_SMOOTHING = 0; //停止状态 protected static final int STATUS_STOP = 1; //ViewGroup宽高 protected int mWidth, mHeight; //变化的marginTop值 protected int mSmoothMarginTop; //默认状态 protected int mStatus = STATUS_STOP; //滚动时间间隔 protected int mDuration = 500; //重复次数 protected int mRepeatTimes = 0; ... @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mWidth = w;mHeight = h;mSmoothMarginTop = -h;initView(); } protected abstract void initView(); ... /*** 是否是奇数圈** @return 结果*/ protected boolean isOddCircle() {return mRepeatTimes % 2 == 1; }
先了解下成员变量,其中最重要的一个就是mSmoothMarginTop,相信很多人都知道一个View的marginTop可以设为负数,这个负数可以给我们带来太多的方便。


上图的图0就是我们展现在屏幕上的ImageView,图1则是屏幕外marginTop为-height的ImageView,这个一定要明白,接下来才好继续实现。
二、装载两个ImageView和一个阴影View

 private List<String> mImgList = new ArrayList<>(); private ImageView[] mImgs = new ImageView[2]; private View mShadowView; ... @Override protected void initView() {//如果没有内容,则不进行初始化操作if (mImgList.size() == 0) { return;}removeAllViews();MarginLayoutParams params = new MarginLayoutParams(mWidth, mHeight);//两个ImageView加载前两张图for (int i = 0; i < mImgs.length; i++) { mImgs[i] = new ImageView(getContext()); addViewInLayout(mImgs[i], -1, params, true); Glide.with(getContext()).load(getImgPath(i)).centerCrop().into(mImgs[i]);}//创建阴影ViewmShadowView = new View(getContext());mShadowView.setBackgroundColor(Color.parseColor("#60000000"));mShadowView.setAlpha(0);addViewInLayout(mShadowView, -1, params, true); } ... /*** 获取图片地址* * @param position 位置* @return 图片地址*/ private String getImgPath(int position) {position = position % mImgList.size();return mImgList.get(position); } 
关键点说明:
MarginLayoutParams 为了之后方便取出margin值
addViewInLayout() 为了对requestLayout的绝对控制
getImgPath() 为了实现循环滚动
这样一来,我们需要的View都已经创建好了。
三、通过一定规律交替控制两个ImageView和它们的marginTop,在onLayout()中实现
 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {int cCount = getChildCount();MarginLayoutParams cParams;for (int i = 0; i < cCount; i++) { View childView = getChildAt(i); cParams = (MarginLayoutParams) childView.getLayoutParams(); int cl = 0, ct = 0, cr, cb; if (isOddCircle()) {if (i == 1) { cl = cParams.leftMargin; ct = mSmoothMarginTop + mHeight;} else if (i == 0) { cl = cParams.leftMargin; ct = mSmoothMarginTop;} } else {if (i == 0) { cl = cParams.leftMargin; ct = mSmoothMarginTop + mHeight;} else if (i == 1) { cl = cParams.leftMargin; ct = mSmoothMarginTop;} } //控制shadowView if (i == 2) {cl = cParams.leftMargin;ct = mSmoothMarginTop + mHeight; } cr = cl + mWidth; cb = ct + mHeight; childView.layout(cl, ct, cr, cb);} }
以上实现的就是不断的替换图1和图2谁上谁下,阴影和下方的图保持同步。
四、marginTop的具体值由属性动画控制,不断调用requestLayout()
先看基类ViewGroup
 /*** 开启滑动**/ public void startSmooth() {if (mStatus != STATUS_STOP) { return;}ValueAnimator animator = ValueAnimator.ofFloat(-mHeight, 0);animator.setDuration(mDuration);animator.setInterpolator(new AccelerateDecelerateInterpolator());animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) {float marginTop = (float) animation.getAnimatedValue();mSmoothMarginTop = (int) marginTop;if (marginTop == 0) { postDelayed(new Runnable() {@Overridepublic void run() { mRepeatTimes++; mSmoothMarginTop = -mHeight; doAnimFinish(); mStatus = STATUS_STOP;} }, 50);} else { doAnim();} }});animator.start();mStatus = STATUS_SMOOTHING; } //动画结束 protected abstract void doAnimFinish(); //动画进行时 protected abstract void doAnim();
关键点说明:
属性动画控制着mSmoothMarginTop在[-mHeight, 0]中变化
每完成一圈,mRepeatTimes自增1

再来看看Gallery实现类
 @Override protected void doAnimFinish() {if (isOddCircle()) { Glide.with(getContext()).load(getImgPath(mRepeatTimes + 1)).centerCrop().into(mImgs[0]);} else { Glide.with(getContext()).load(getImgPath(mRepeatTimes + 1)).centerCrop().into(mImgs[1]);}mShadowView.setAlpha(0); } @Override protected void doAnim() {mShadowView.setAlpha(((1 - (-mSmoothMarginTop) / (float) mHeight)));requestLayout(); }
关键点说明:
通过mSmoothMarginTop与mHeight的比值控制阴影View的透明度
每次动画完成时,下方的图(此时下方的图已经超出屏幕了,而上方图显示在屏幕内)需要加载第三张图,使用getImgPath()取出
pic1


以上就是图片的滚动实现,文字的滚动90%是一样的,有点区别的就是需要文字需要控制下垂直居中,我就不赘述了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。