1.前言
在Android设备上由于手机屏幕尺寸的局限性,为了给用户呈现更多的内容就需要使用滑动的操作去隐藏或者显示一些内容。但是不管是多么绚丽的滑动效果,归根结底都是由不同的滑动加上一些特效组成的,因此滑动在Android中具有很重要的效果。View的滑动一般可以通过View本身提供的scrollTo/scrollBy方法实现、通过动画给View添加平移效果、通过改变View的LayoutParams使View重新布局这三种方法实现。
2.使用scrollTo/scrollBy
为了实现View的滑动,View类中提供了scrollTo和scrollBy来实现这个功能
scrollTo(int x, int y) — 设置滚动视图的位置,这将会导致调用onScrollChanged(int,int,int,int)和视图失效。
scrollBy(int x, int y) — 移动滚动视图的位置。这将会导致调用onScrollChanged(int,int,int,int)和视图失效。
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
/**
* Move the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically
*/
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
从这两个方法的源码中可以看出,scrollBy实际上也是调用了scrollTo方法,它实现了基于当前位置的相对滑动,而scrollTo则实现了基于所传参数的绝对滑动。但是二者都只能改变View内容的位置而不能改变View在布局中的位置。
实现的效果为:
实现的代码为:
package com.example.administrator.viewscrolldemo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
/**
* Created by ChuPeng on 2017/1/18.
* 通过ScrollTo/ScrollBy对View进行移动
* 这个方法只能移动View的内容,不能将View整体移动
*/
public class ScrollActivity extends Activity
{
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scroll);
final Button ScrollToBtn = (Button) findViewById(R.id.ScrollTo);
final Button ScrollByBtn = (Button) findViewById(R.id.ScrollBy);
ScrollToBtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ScrollToBtn.scrollTo(100, 100);
}
});
ScrollByBtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ScrollByBtn.scrollBy(100, 100);
}
});
}
}
3.使用动画
使用动画可以让一个View进行平移,其实平移也是一种滑动。使用动画对一个View进行平移,主要是操作View的translationX和translationY属性,在这里既可以用传统的View动画也可以用属性动画。
不过在使用View动画并不会真正的改变View的位置,这是一个很严重的问题,如果在一个View上面设置单击事件,在使用View动画进行平移以后你会发现尽管View已经不在原来的位置了,但是新的View无法触发onClick事件,而单击原始位置仍然可以触发onClick事件,这个问题所带来的影响是致命的。其实,在系统眼里这个View的位置信息并不会随着View动画的改变而改变,因此这个View并没有发生任何变化,它的真身仍然在原始位置,新的位置只不过是View的影像而已,因此,我们不能简单地给一个View通过View动画进行平移并且还希望在它的新位置触发单击事件,如果使用属性动画进行平移就不会出现这些问题。
实现效果为:
实现代码为:
package com.example.administrator.viewscrolldemo;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
/**
* Created by ChuPeng on 2017/1/18.
* 通过动画对View进行移动
*/
public class AnimationActivity extends Activity
{
private ImageView imageView;
private WindowManager manager;
private int screenWidth;
private int screenHeight;
private int imageViewWidth;
private int imageViewHeight;
private int translationX;
private int translationY;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
imageView = (ImageView) findViewById(R.id.imageView);
manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
//得到屏幕的宽
screenWidth = manager.getDefaultDisplay().getWidth();
//得到屏幕的高
screenHeight = manager.getDefaultDisplay().getHeight();
imageView.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
//得到ImageView的宽
imageViewWidth = v.getWidth();
//得到ImageView的高
imageViewHeight = v.getHeight();
//计算平移的长
translationX = screenWidth - imageViewWidth;
//计算平移的宽
translationY = screenHeight - imageViewHeight;
AnimatorSet animatorTransactionSet = new AnimatorSet();
//对集合中定义的动画依次播放
animatorTransactionSet.playSequentially(
//平移动画,依次播放
ObjectAnimator.ofFloat(imageView, "translationX", 0, translationX),
ObjectAnimator.ofFloat(imageView, "translationY", 0, translationY),
ObjectAnimator.ofFloat(imageView, "translationX", translationX, 0),
ObjectAnimator.ofFloat(imageView, "translationY", translationY, 0)
);
//设置动画时间
animatorTransactionSet.setDuration(2000).start();
AnimatorSet animatorRotateSet = new AnimatorSet();
animatorRotateSet.playTogether(
//旋转动画,同时播放
ObjectAnimator.ofFloat(imageView, "rotationX", 0, 360),
ObjectAnimator.ofFloat(imageView, "rotationY", 0, 360)
);
//设置动画时间
animatorRotateSet.setDuration(2000*4).start();
}
});
}
}
4.改变布局参数
改变布局参数的方法就是改变LayoutParams,这中方式应该比较好理解。如果我们想把一个View向右平移100px,我们只需要将这个View的LayoutParams里的marginLeft的参数增加100px。或者还可以在这个View的左边再放置一个空View,开始的时候这个空View的默认宽度为0,当需要向右移动View时,只需要重新设置空View的宽度即可,当空View的宽度增大时,需要移动的View就会被挤向右边这样就实现了向右移动的效果。
实现效果为:
实现代码为:
package com.example.administrator.viewscrolldemo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
/**
* Created by ChuPeng on 2017/1/18.
* 通过改变布局参数对View进行移动
*/
public class LayoutParamsActivity extends Activity
{
//向上移动
private Button up;
//向下移动
private Button down;
//重置
private Button reset;
//被移动的按钮
private Button moveButton;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_params);
up = (Button) findViewById(R.id.up);
down = (Button) findViewById(R.id.down);
reset = (Button) findViewById(R.id.reset);
moveButton = (Button) findViewById(R.id.moveButton);
up.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
//获取被移动按钮的参数
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) moveButton.getLayoutParams();
//每次移动100px
params.topMargin -= 100;
//刷新View控件
moveButton.requestLayout();
}
});
down.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
//获取被移动按钮的参数
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) moveButton.getLayoutParams();
//每次移动100px
params.topMargin += 100;
//刷新View控件
moveButton.requestLayout();
}
});
reset.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
//获取被移动按钮的参数
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) moveButton.getLayoutParams();
//重置为0
params.topMargin = 0;
//刷新View控件
moveButton.requestLayout();
}
});
}
}
5.总结
使用scrollTo/scrollBy这种方式进行滑动可以比较方便的实现滑动效果并且不会影响内部元素的单击事件,但是它只能滑动View的内容并不能滑动View本身。
使用属性动画进行滑动没有明显的缺点,但是要是使用View动画进行滑动,只能实现滑动效果而不能对点击事件产生好的响应,不能响应用户的交互。
使用改变布局这种方式,它除了使用起来比较麻烦以外并没有明显的缺点,但是,如果要实现一些比较复杂的效果就显得力不从心了,这时就必须要靠动画去完成。
以上Demo的源代码地址:点击打开链接