在了解了前面介绍的基础知识之后,下面就来看看具体的滑动实现,接下来会介绍7种方法,主要是结合《Android群英传》中学习的知识展开。这七种方法分别是:

(1)View绘制时的layout方法

(2)View绘制时根据系统封装好的offSetLeftAndRight以及offSetTopAndBottom接口实现View的布局定位

(3)通过View的布局参数layoutParams来重置View的margin值实现布局重新定位

(4)通过View的scrollTo和scrollBy方法

(5)通过Scroller类实现

(6)通过属性动画实现

(7)通过ViewDragHelper实现

对于滑动的实现,我会分为四个部分讲解,*部分讲述(1)(2)(3)的实现方法,第二部分讲述(4)和(5)的实现,第三部分讲述属性动画方法实现,第四部分也是较复杂的部分讲述(7)的实现,下面我们就开始吧。本部分主要介绍scrollTo和scrollBy以及Scroller使用

1,scrollTo和scrollBy
任何一个View子类对象都可以调用这两个接口

1.1 两者的关系
scrollBy源码:

/**
* 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

1.2 两者的作用和区别
(1)scrollTo(),view移动到指定坐标点

(2)scrollBy(),view依据当前的坐标点结合传入的偏移量进行移动

移动的效果:

如果是ViewGroup,那么屏幕展示的移动效果实际上是移动的其子view

如果是ImageView,Button这样的控件,那么屏幕展示的移动效果实际上是移动的其Content

比如下图示例:

%title插图%num

我们移动的View,本质上移动的却是其Content,所以如果我们想实现移动一个View的效果,只需要移动其父布局即可。需要注意的是这里有一个相对的概念,也即子View向左移动,父布局就需要向右移动;上下也是同理。下面就来看看具体的示例:

2,代码示例
2.1 简单移动测试
ScrollActivity代码:

package com.hfut.operationscroll;

import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

/**
* @author why
* @date 2018-8-19 16:48:41
*/
public class ScrollActivity extends AppCompatActivity {

int lastX = 0;
int lastY = 0;
private static final String TAG = “MainActivity”;
Button button;
LinearLayout linearLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scroll);
button = findViewById(R.id.test_button);
linearLayout=findViewById(R.id.test_layout);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, “onTouch: “+ System.currentTimeMillis());
//方式一,通过视图坐标系计算偏移量
int offX = x – lastX;
int offY = y – lastY;
v.scrollBy(-offX,-offY);
linearLayout.scrollBy(-offX,-offY);
break;
default:
break;
}
return true;
}
});
}

}
activity_scroll.xml代码:

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:id=”@+id/test_layout”
android:orientation=”vertical”
tools:context=”.MainActivity”>

<Button
android:id=”@+id/test_button”
android:layout_width=”400dp”
android:layout_height=”400dp”
android:text=”拖我移动” />

</LinearLayout>
代码和*部分差不多,偏移量的计算都一样,其中:

case MotionEvent.ACTION_MOVE:
Log.d(TAG, “onTouch: “+ System.currentTimeMillis());
int offX = x – lastX;
int offY = y – lastY;
v.scrollBy(-offX,-offY);
linearLayout.scrollBy(-offX,-offY);
break;
部分,里面两行关于移动的代码,如果把*行注释掉,那么按钮和里面的text一起同步移动,如果两行全放开,那么按钮里面的text也会相对button边框移动,运行代码:

%title插图%num

v.scrollBy(-offX,-offY);
注释后:

%title插图%num

注释前:

%title插图%num

2.2 使用scrollBy或者scrollTo模拟平滑滑动
我们知道,使用这两个接口实现的滑动都是瞬间完成的,给客户的体验很不好,下面就结合这两个方法以及Timer和Handler实现一个平滑的滑动,因为都是基础知识,这里直接给出代码:

ScrollActivity代码:

package com.hfut.operationscroll;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Scroller;
import android.widget.Switch;

import java.sql.Time;
import java.util.Timer;
import java.util.TimerTask;

/**
* @author why
* @date 2018-8-19 17:14:10
*/
public class ScrollActivity extends AppCompatActivity {

public static final int DESTINATION=1;
public static final int STARTPOINT=0;
int quickCount = 1;
int slowCount = 1;
LinearLayout quickLinearLayout;
LinearLayout slowLinearLayout;
Switch slowOne;
Switch quickOne;
Scroller scroller;
Button quickTrigger;
Button slowTrigger;
int movedDis = 0;
int x = 0;
int increment = 10;

Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DESTINATION:
slowTrigger.setText(“回来”);
slowOne.setChecked(true);
break;
case STARTPOINT:
slowTrigger.setText(“慢移”);
slowOne.setChecked(false);
break;
default:
break;
}
}
};

private static final String TAG = “ScrollerActivity”;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scroller);
quickLinearLayout = findViewById(R.id.quick_move_layout);
slowLinearLayout = findViewById(R.id.slow_move_layout);
slowOne = findViewById(R.id.slow_runner);
quickOne = findViewById(R.id.quick_runner);
quickTrigger = findViewById(R.id.quick_button);
slowTrigger = findViewById(R.id.slow_button);
x = (int) quickOne.getX();
}

public void quickMove(View view) {

if (quickCount % 2 == 1) {
//Log.d(TAG, “quickMove: ” + total);
//考虑到其本身的长度,所以这里减少了100sp
quickLinearLayout.scrollBy(-(650 – x – 100), 0);
quickOne.setChecked(true);
quickTrigger.setText(“回来”);
} else {
quickLinearLayout.scrollBy(650 – x – 100, 0);
quickOne.setChecked(false);
quickTrigger.setText(“瞬移”);
}
quickCount++;
}

public void slowMove(View view) {
if (slowCount % 2 == 1) {
movedDis = 0;
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (540 – x >= movedDis) {
movedDis += increment;
slowLinearLayout.scrollBy(-increment, 0);
} else {
Message message=new Message();
message.what=DESTINATION;
handler.sendMessage(message);
timer.cancel();
}
}
}, 200, 50);
} else {
movedDis = 0;
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (540 – x >= movedDis) {
movedDis += increment;
slowLinearLayout.scrollBy(increment, 0);
} else {
Message message=new Message();
message.what=STARTPOINT;
handler.sendMessage(message);
timer.cancel();
}
}
}, 200, 50);
}
slowCount++;
}
}

activity_scroll.xml代码:

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
tools:context=”com.hfut.operationscroll.ScrollActivity”>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<Button
android:text=”瞬移”
android:id=”@+id/quick_button”
android:onClick=”quickMove”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />
<LinearLayout
android:id=”@+id/quick_move_layout”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<Switch
android:id=”@+id/quick_runner”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginTop=”15sp” />
</LinearLayout>

</LinearLayout>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<Button
android:text=”慢移”
android:id=”@+id/slow_button”
android:onClick=”slowMove”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />

<LinearLayout
android:id=”@+id/slow_move_layout”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>

<Switch
android:id=”@+id/slow_runner”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginTop=”15sp” />
</LinearLayout>

</LinearLayout>

</LinearLayout>

运行结果:

%title插图%num

这部分主要还是理解使用这种移动的原理,这对于后面的难点滑动冲突问题的解决至关重要。
————————————————