自定义View之图片随手势方向动态加载
直接看实现出来的效果吧~
![slipView动画]](https://img.haomeiwen.com/i2099469/dd8526fae911645a.gif?imageMogr2/auto-orient/strip)
图片和图片的标题会根据手势的不同以不同的方式动态加载,标题伴随透明变化,图片则有一定的阴影效果。
第一次看到这个效果是在JeasonWong的个人博客里面,但是具体的地址不记得了~
我在看了原作者的效果之后感觉很不错,于是就自己做了一些手势上的扩展,图片的加载使用Glide库
分析
- 图片的滑动效果和文字的滑动效果基本一致,但是为了日后使用方便,把图片和文字的实现代码分离,但是可以使用模板模式简化重复的部分。
- 图片和文字的动态切换效果,使用ViewGroup中放置两个TextView和ImageView来动态的隐藏和显示实现,另外图片在切换的时候会有第三个ImageView作为遮罩层
撸代码
0x01
首先是两个辅助类,SlipTo枚举滑动的方向,BaseEntey方便信息的传递。分别对应图片链接和图片标题
/**
* 滑动方向
*/
public enum SlipTo {
UP,
DOWN,
LEFT,
RIGHT
}
public class BaseEntry {
public String pathurl;
public String title;
‘’‘省略’‘’
}
0x02
SlipBase类作为SlipImage和SlipText的父类,使用监听属性动画的更新状态间接更新ViewGroup中对应子View的状态。实现动态的效果
doAnimFinish()和 doAnim()方法则是留给子类分别实现自己的逻辑
/**
* 滑动的方法
*/
public void slip(){
if (isSlip){
return;
}
isSlip = true;
ValueAnimator valueAnimator = null;
switch (slipTo){
case DOWN:
case UP:
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();
}
}
});
break;
case LEFT:
case RIGHT:
valueAnimator = ValueAnimator.ofFloat(-mWidth,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 = - mWidth;
mRepeatCount++;
doAnimFinish();
isSlip = false;
}
},50);
}else{
doAnim();
}
}
});
break;
}
valueAnimator.start();
}
0x03
SlipImage就是实现图片滑动的逻辑的主要类,SlipText和她基本一样这里就主要介绍下这个类好了
initView添加三个ImageView到ViewGroup里面,两个负责装载图片,一个是遮罩层
doAnim()方法中设置遮罩层的透明度,并且调用requestLayout不断刷新界面
doAnimFinish()里面则加载图片并且移除遮罩层
其实主要的图片显示逻辑在onLayout方法里面,根据不同的状态设置不同的位置参数,从而实现视图的显示和隐藏。
@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);
}
@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);
}
@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();
}
@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;
}
}
0x04
在SlipView中将 SlipImage和SlipText组合,统一调度并添加手势控制
SlipView还有公开方法:setData用于设置数据,slipTo用于使用者自行控制图片动态加载方向
/**
* 通过滑动事件 控制图片加载方向
* @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;
}
0x05
使用
//设置数据
slipViewImage.setData(data);
//自行控制图片加载方向
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
slipViewImage.slipTo(SlipTo.DOWN);
}
});
网友评论