这个效果最初是在 http://www.wangyuwei.me/ 看到和学习的,不过我自己做了一些手势上的扩展。 感谢JeasonWong
先放效果图:
图片和图片的标题会根据手势的不同而已不同的方式加载,标题伴随透明变化,图片则有一定的阴影效果。
step1:
标题的透明度变化,图片的阴影以及图片和标题的滑动,考虑使用监听属性动画的更新状态来实现。因为标题和图片的功能大致相似,所以这里抽出SlipBase类作为基类,实现公共部分。
SlipBase核心代码
valueAnimator = ValueAnimator.ofFloat(-mHeight,0)
.setDuration(mDuration);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float margintop = (float)valueAnimator.getAnimatedValue();
mMarginTopHeight = (int)margintop;
if(margintop == 0){
postDelayed(new Runnable() {
@Override
public void run() {
mMarginTopHeight = - mHeight;
mRepeatCount++;
//动画执行完毕 更新状态
doAnimFinish();
isSlip = false;
}
},50);
}else{
//动画执行中 不断更新状态
doAnim();
}
}
});
其中mMarginTopHeight 是为了计算ViewGroup里面的子View距离ViewGroup顶部或者右边的变量。另外这里展示出来的只是上下滑动时需要的属性动画,左右滑动时大同小异,详细的请下载代码查看。
这里说明一下:子View的隐藏这里都是使用负margintop实现的。
step2:
分别在SlipImage(控制图片)和SlipText(控制标题)里面实现
<span style="font-family:Microsoft YaHei;font-size:18px;">//动画执行完毕 更新状态
doAnimFinish();</span>
<span style="font-family:Microsoft YaHei;font-size:18px;">和</span>
<pre name="code" class="java"><span style="font-family:Microsoft YaHei;font-size:18px;"> //动画执行中 不断更新状态
doAnim();</span>
这两个方法
以SilpImage类为例
首先初始化自定义View 两个加载图片的ImageView和一个阴影ImageView
@Override
protected void initView() {
if(list.size()==0) return;
removeAllViews();
MarginLayoutParams marginLayoutParams = new MarginLayoutParams(mWidth,mHeight);
for(int i=0; i<imageViews.length; i++){
imageViews[i] = new ImageView(getContext());
//加载图片
Glide.with(getContext()).load(getCurrentPath(i)).dontTransform().dontAnimate().centerCrop().into(imageViews[i]);
addView(imageViews[i],-1,marginLayoutParams);
}
ImageView imageView = new ImageView(getContext());
imageView.setBackgroundColor(Color.parseColor("#90000000"));
imageView.setAlpha(0f);
addView(imageView,-1,marginLayoutParams);
}
先看doAnimFinish方法 比较容易看
@Override
protected void doAnimFinish() {
//根据循环的次数加载图片 实现循环加载
if(isEvenRepeat()){
Glide.with(getContext()).load(getCurrentPath(mRepeatCount+1)).dontTransform().centerCrop().dontAnimate().into(imageViews[1]);
}else{
Glide.with(getContext()).load(getCurrentPath(mRepeatCount+1)).dontTransform().centerCrop().dontAnimate().into(imageViews[0]);
}
//去除阴影
getChildAt(2).setAlpha(0f);
}
再看doAnim方法,不断的请求更新UI
@Override
protected void doAnim() {
switch (slipTo){
case DOWN:
case UP:
getChildAt(2).setAlpha(1-(-mMarginTopHeight/(float)mHeight));
break;
case LEFT:
case RIGHT:
getChildAt(2).setAlpha(1-(-mMarginTopHeight/(float)mWidth));
break;
}
requestLayout();
}
最终导致界面UI变化的肯定就是onLayout方法了,瞅一眼,代码虽然较多但是都是大白话一样,就是根据mMarginTopHeight以及手势和循环的次数控制子View的位置参数。
@Override
protected void onLayout(boolean changeed, int l, int t, int r, int b) {
if(getChildCount()<3) return;
MarginLayoutParams marginLayoutParams;
switch (slipTo){
case DOWN:
for (int i=0;i<getChildCount(); i++){
View childView = getChildAt(i);
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int cl= marginLayoutParams.leftMargin, ct = 0, cr = cl + mWidth, cb;
if(isEvenRepeat()){
if(i==0) {
ct = mMarginTopHeight + mHeight;
}else if(i==1){
ct = mMarginTopHeight;
}
}else{
if(i==0){
ct = mMarginTopHeight;
}else if(i==1){
ct = mMarginTopHeight + mHeight;
}
}
if(i==2){
ct = mMarginTopHeight + mHeight;
}
cb = ct + mHeight;
childView.layout(cl,ct,cr,cb);
}
break;
case UP:
for (int i=0;i<getChildCount(); i++){
View childView = getChildAt(i);
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int cl= marginLayoutParams.leftMargin, ct = 0, cr = cl + mWidth, cb;
if(isEvenRepeat()){
if(i==0) {
ct = -mHeight-mMarginTopHeight;
}else if(i==1){
ct = -mMarginTopHeight;
}
}else{
if(i==0){
ct = -mMarginTopHeight;
}else if(i==1){
ct = -mMarginTopHeight - mHeight;
}
}
if(i==2){
ct = -mMarginTopHeight - mHeight;
}
cb = ct + mHeight;
childView.layout(cl,ct,cr,cb);
}
break;
case RIGHT:
for (int i=0;i<getChildCount(); i++){
View childView = getChildAt(i);
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int cl= 0, ct = marginLayoutParams.topMargin, cr , cb = ct + mHeight;
if(isEvenRepeat()){
if(i==0) {
cl = mMarginTopHeight+mWidth;
}else if(i==1){
cl = mMarginTopHeight;
}
}else{
if(i==0){
cl = mMarginTopHeight;
}else if(i==1){
cl =mMarginTopHeight+mWidth;
}
}
if(i==2){
cl = mMarginTopHeight+mWidth;
}
cr = cl + mWidth;
childView.layout(cl,ct,cr,cb);
}
break;
case LEFT:
for (int i=0;i<getChildCount(); i++){
View childView = getChildAt(i);
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
int cl= 0, ct = marginLayoutParams.topMargin, cr , cb = ct + mHeight;
if(isEvenRepeat()){
if(i==0) {
cl = -mMarginTopHeight-mWidth;
}else if(i==1){
cl = -mMarginTopHeight;
}
}else{
if(i==0){
cl = -mMarginTopHeight;
}else if(i==1){
cl = -mMarginTopHeight-mWidth;
}
}
if(i==2){
cl = -mMarginTopHeight-mWidth;
}
cr = cl + mWidth;
childView.layout(cl,ct,cr,cb);
}
break;
}
step3:
在SlipView中将 SlipImage和SlipText组合,统一调度并添加手势控制
判断手势的代码如下
/**
* 通过滑动事件 控制图片加载方向
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
downY = (int) event.getRawY();
downX = (int) event.getRawX();
return true;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
upY = (int) event.getRawY();
upX = (int) event.getRawX();
if(Math.abs(downY-upY)>y_limit){
if((downY - upY) >0)
slipTo(SlipTo.UP);
else
slipTo(SlipTo.DOWN);
}
if(Math.abs(downX-upX)>x_limit){
if((downX-upX) >0)
slipTo(SlipTo.LEFT);
else
slipTo(SlipTo.RIGHT);
}
break;
}
return false;
}
step4
在使用的时候直接放入数据调用
slipViewImage.setData(data);
当然这只是实现这种效果的一种方式而已,主要是练习一下自定义View的写法。
如果对这个小View有兴趣也可以去下载源码看一看,改一改。
github下载 点击打开链接
csdn下载 源码