原来操控界面可以这么简单----安卓上下滑动缩放顶部图片,左右滑动结束当前Activity,及View柔和回弹效果

2 篇文章 0 订阅
1 篇文章 0 订阅

先上效果图:

上传图片不能超过2M,费了好大劲。每一张gif动的有点快,将就看。

首先说原理:

为activity的xml文件根布局添加setOnTouchListener。上下滑动和左右滑动的所有操作都是在OnTouchListeneronTouch方法中实现的,通过计算上下左右滑动的距离来操作View的。

一共有两个界面,第一个是LoginActivity,什么用也没有,就是在里面的button,点击跳转到MainActivity.MainActivity是主要的。


主要代码:

LoginActivity:

mButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
               startActivity(new Intent(this,MainActivity.class));

            }
        });



activity_main:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/img"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:scaleType="centerCrop"
        android:src="@drawable/xx"
        android:layout_gravity="center_horizontal"
        />
    <LinearLayout android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:background="@color/colorPrimary"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:text="Hello World!" />
    </LinearLayout>
</LinearLayout>

先获取activity_main.xml的根布局View,然后为它添加Touch事件监听,之后所有的操作都会在其中进行。

root_ll = (LinearLayout) findViewById(R.id.activity_main);
root_ll.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        //代码在这里
        return true;
    }
});
注意ontouch方法一定要返回true。
 
 

先说上下滑动缩放图片:

分为两部分,
下滑时:图片会放大,宽度和高度都会变化,并且图片和下面的helloworld区域存在一种类似图层叠加透视的关系,仔细看效果图,图片和下面的区域并不是像铺地板一样,一块和另一块紧挨在一起。而是随着图片放大,这两部分看起来像两层,就好像下面的区域是覆盖在图片上一样。
上滑时,图片没有缩放,并且高度变小,宽度保持不变。
 

布局文件:

关于图片的缩放,只要在布局文件中设置
android:scaleType="centerCrop"既可。这样我们在缩放的时候只要控制ImageView的宽高度就可以,图片会自动追随ImageView的变化而放大缩小。
布局到这就结束了,回到代码。

第一步,得到ImageView正常时的宽高度:

ImageView img;
int width;
int height;
img = (ImageView) findViewById(R.id.img);
ViewTreeObserver viewTreeObserver=img.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onGlobalLayout() {
        width = img.getMeasuredWidth();
        height = img.getMeasuredHeight();
        img.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
});
应该知道这里不能通过img.getWidth()方法获得吧。因为这时候图片还没有绘制出来,得到的是0.
img.getViewTreeObserver().removeOnGlobalLayoutListener(this);
这一局要有,是在得到宽高度之后取消监听,不然之后每当img属性发生变化时都会执行这个方法。

第二部,在onTouch中开始操作:

整个触摸屏幕的过程分为down,move,up三个阶段。
在down的时候得到参考点:
if (event.getAction()==MotionEvent.ACTION_DOWN) {
    starty = event.getY();
    startx = event.getX();
}
然后在滑动的时候实时获取x,y点,来缩放ImageView
if (event.getAction()==MotionEvent.ACTION_MOVE) {
    
        tw = (int) (width * ((event.getY() - starty) / 1000 + 1));
        th = (int) (height * ((event.getY() - starty) / 1600 + 1));
        if (th<=0) {
            th=1;
        }

        if (th < height) {
            layoutParams.width = width;
            layoutParams.height = th;
        } else {
            layoutParams.width = tw;
            layoutParams.height = th;
        }
        img.setLayoutParams(layoutParams);
    } 
这里是根据上下滑动的距离来缩放,所以(event.getY() - starty) / 1000是得到上下滑动的距离,除以1000,只是降低一下幅度而已,也可以换成其它的数值,这样得到的tw就是width放大了多少倍。特别需要注意,
th = (int) (height * ((event.getY() - starty) / 1600 + 1));这里高度除以的是1600,要比1000大,正是这个不一样实现了图片看起来是图层叠加的效果,如果我没有说清的话,你可以把高度也设置成除以1000.看看不同就明白了。
获得到缩放后的宽高后,和图片的原始高度比较一下,if (th < height)就知道是上滑还是下滑,然后设置一下宽高度就可以了。
 

第三步,抬起手指时的回弹效果:

if (event.getAction()==MotionEvent.ACTION_UP) {
     if (th < height / 2) {
            back2Origin(width, th, width, 1);
        } else if (th < height) {
            back2Origin(width, th, width, height);
        } else {
            back2Origin(tw, th, width, height);
        }
这里也有一个简单的判断,如果此时高度小于原始高度一半了,那就让高度渐变到1,如果不是就回弹回原来的高度,这样就可以实现上滑下滑的动画效果,我觉得这里的功臣就是
android:scaleType="centerCrop"这行。解决了图层叠加,遮挡的这种效果问题。
回弹,我用到的是属性动画,后面拿出来。
这样,上下滑动的效果就实现了。

左右滑动:

第一步,设置activity主题为透明:

左右滑动时我们会看到下面露出了之前的activity的界面,这是因为设置了当前的activity主题为透明,然后通过设置TranslutionX来实现左右滑动效果。
自定义一个透明样式:
在styles.xml文件中
<style name="myTransparent" parent="@style/AppTheme">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
    <item name="windowNoTitle">true</item>
</style>
在manifest文件中为MainActivity使用自定义的样式:
<activity
    android:name=".MainActivity"
    android:theme="@style/myTransparent"
    >
</activity>
设置完毕,回到onTouch代码中。

第二步,实现左右滑动:

if (event.getAction()==MotionEvent.ACTION_MOVE) {   
	root_ll.setTranslationX(currentTranslationX+(event.getX() - startx)/2f);
}
(event.getX() - startx)/2f是得到横向的滑动距离,也是降低了一下幅度,currentTranslationX是root_ll当前的偏移数,
就这么简单的一设置,就可以实现activity左右不停偏移,露出之前的activity了。此时还没有回弹效果,这里也是用到了属性动画。一会说。
这里还有一个小问题,看图,顶部的状态栏并没有随着一起运动,体验很差。


这就涉及到一个问题,就是我们为activity setContentView的xml并不是activity的全部,这只是activity布局的一个子View而已,具体可以搜索详解。
所以这里我们要得到activity真正的加载View,
View root=this.getWindow().getDecorView();这个root就可以了。

第三步,添加回弹效果:

if (event.getAction()==MotionEvent.ACTION_UP) {
    
        currentTranslationX = root.getTranslationX();
        handleTranslationX();
}
handleTranslationX()方法来处理的回弹动画,这样左右滑动也监听实现了。

组合:

单个实现上下滑或左右简单,但是组装在onTouch方法中,就会有问题了,我们可以简单的判断一下:
if (event.getAction()==MotionEvent.ACTION_MOVE) {    
        if (Math.abs(event.getY() - starty )>=Math.abs( event.getX() - startx)) {
            vertical=true;
        } else {
            vertical=false;
    }
if (vertical) {
    Log.i("xx", "" + event.getY());
    tw = (int) (width * ((event.getY() - starty) / 1000 + 1));
    th = (int) (height * ((event.getY() - starty) / 1600 + 1));
    if (th<=0) {
        th=1;
    }

    if (th < height) {
        layoutParams.width = width;
        layoutParams.height = th;
    } else {
        layoutParams.width = tw;
        layoutParams.height = th;
    }
    img.setLayoutParams(layoutParams);
} else {
    root.setTranslationX(currentTranslationX+(event.getX() - startx)/2f);
}

vertical:boolean值,表示上下滑动,先根据方向的距离大小判断出是横向还是纵向,然后根据virtical来分离操作。这样会有问题的,可以实验一下,会发现因为move是在不断调用的,所有即使一开始是左右滑,一旦手势上下了virtical也会被置true,就会导致一会执行左右滑,一会执行上下滑。所以我们需要一个类似网络会话中session的东西,来告诉ontouch,一旦开始时判断出是哪个方向,就在这次不断的滑动当中,始终忽略另一个方向的滑动,直到抬起手指,这次滑动结束。

所以添加另一个boolean值thisTime判断:
if (event.getAction()==MotionEvent.ACTION_MOVE) {
    if (!thisTime) {
        thisTime=true;
        if (Math.abs(event.getY() - starty )>=Math.abs( event.getX() - startx)) {
            vertical=true;

        } else {
            vertical=false;

        }
    }
这一段代码,一旦判断出virtical的值,同时thisTime=true,这样在之后的滑动当中就不会再执行判断virtical的代码,virtical值就一直不会改变了。解决了一会上下滑 一会左右滑的问题。
直到抬起手指:
if (event.getAction()==MotionEvent.ACTION_UP) {    
    thisTime=false;
}
表示这个滑动结束了,这样当下次再按下手指滑动时,就又会重新判断一次virtical,一次新的单向滑动开始了。

回弹动画:

用的是属性动画中的ValueAnimator。我就直接贴代码了。解释在注释里。具体的属性动画详情也可以搜索。
/**
 * 
 * @param fromw 起始宽度
 * @param fromh 起始高度
 * @param tow 结束宽度
 * @param toh 结束高度
 */
public void back2Origin(final int fromw, final int fromh, final int tow, int toh) {
        final int ws=tow-fromw;
    final int hs=toh-fromh;
//创建一个ValueAnimator,让他的值从0变化到100.
    ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100);
//为valueAnimator添加一个事件监听,每次数值变化时都会执行这个方法。
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
//animation.getAnimatedValue(),取得此时的数值(0-100)
            layoutParams.height=fromh+(int)((int)animation.getAnimatedValue()/100f*hs);
            layoutParams.width=fromw+(int)((int)animation.getAnimatedValue()/100f*ws);
            img.setLayoutParams(layoutParams);
        }
    });
//设置这个动画的时常
    valueAnimator.setDuration(500);
//开始执行动画
    valueAnimator.start();
}

/**
 * 这里判断右滑的距离,如果超过宽度四分之一了,就向右滑到底,并结束
 * 如果不是就回弹
 */
public void handleTranslationX() {
    Point p = new Point();
//取得屏幕的宽度,存放在point中
    getWindowManager().getDefaultDisplay().getSize(p);
    if (Math.abs(currentTranslationX) > p.x / 4f) {
        slideWithFinish(p.x);
    } else {
        slideWithFinish(0f);
    }
}

/**
 * 左右滑动的动画
 * @param endX 结束时的偏移量
 */
public void slideWithFinish(final float endX) {
    ValueAnimator valueAnimator = ValueAnimator.ofFloat(currentTranslationX, endX);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            root.setTranslationX((float)animation.getAnimatedValue());
//这里判断一下动画结束时是否是finish当前activity
            if ((float)animation.getAnimatedValue()==endX) {
                if (endX > currentTranslationX) {
                    MainActivity.this.finish();
                } else {
                    currentTranslationX = endX;
                }
            }

        }
    });
    valueAnimator.setDuration(500);
    valueAnimator.start();
}


到这就结束了,其实很简单吧。代码写的不完善,比如左右滑时可以向左滑,一些小情况没有判断。写的不对的地方请大家指教,如果帮到你了,
给我一个赞,小小的顶我一下吧!谢谢啦୧༼ʘ̆ںʘ̆༽୨
代码在这里

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值