效果图:
项目结构:
代码:
MainActivity
package com.example.scrolltest;
import android.app.Activity;
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;
/*
1. 瞬间移动视图的内容: 利用View的scroll方法
1). scrollBy(int x, int y) : 滑动指定的偏移量(从当前位置瞬间)
x: x轴上的偏移量, x>0内容向左滑动, x<0内容向右滑动, x=0水平方向不滑动
y: y轴上的偏移量, y>0内容向上滑动, y<0内容向下滑动, y=0垂直方向不滑动
2). scrollTo(int x, int y) : 滑动到指定的偏移量(从当前位置瞬间)
x: 目标位置x轴上的偏移量, x>0移动到原始位置的左侧, x<0移动到原始位置的右侧,x=0移动到水平原始位置,
y: 目标位置y轴上的偏移量, y>0移动到原始位置的上侧, y<0移动到原始位置的下侧, y=0移动到垂直原始位置
2. 平滑移动视图的内容: 利用Scoller和View的scroll方法
1). Scoller是实现View平滑移动的帮助类, 它本身并不能实现对View的移动
2). 平滑移动的基本原理: 将整个从起始位置到结束位置的移动分解成多个小的距离, 循环调用scrollTo()实现平滑移动
3). 相关API:
a. Scoller类:
-->Scoller(Context context) : 创建对象的构造方法
-->startScroll(int startX, int startY, int dx, int dy, int duration) : 开始平滑移动视图(这个方法本身不会产生滑动)
startX : 起始位置的X偏移量
startY : 起始位置的Y偏移量
dx: 滑动多大的X偏移量(如果是0,X方向不会滑动)
dy: 滑动多大的Y偏移量(如果是0,Y方向不会滑动)
duration : 整个过程持续的时间(ms)
-->startScroll(int startX, int startY, int dx, int dy): 开始平滑移动视图(时间为250ms)
-->boolean computeScrollOffset() : 计算当前移动的偏移量, 并将其保存到Scoller对象中, 如果滑动还没有完成返回true
-->int getCurrX() : 得到计算出的X偏移量
-->int getCurrY() : 得到计算出的Y偏移量
b. View类
-->invalidate() : 强制重绘, 导致draw()-->computeScroll()
在scoller.startScroll()后必须执行此方法
-->computeScroll() : 需要重写此方法, 用于计算移动, 此方法在draw()中调用
调用scoller计算移动偏移量
调用view对象scrollTo()到计算出的偏移量
调用View对象invalidate()强制重绘, 导致computeScroll()再次执行
*/
// 对应视频
// http://www.gulixueyuan.com/course/124/learn#lesson/1910
// scrollBy的使用
public class MainActivity extends Activity {
private Button btnScrollLeft;
private Button btnScrollRight;
private Button btnScrollUp;
private Button btnScrollDown;
private Button btnReset1;
private Button btnReset2;
private MyImageView ivMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
myOnclick();
}
private void myOnclick() {
btnScrollLeft.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ivMain.scrollBy(10, 0);
Log.e("TAG", ivMain.getScrollX() + "-");
}
});
btnScrollRight.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ivMain.scrollBy(-10, 0);
Log.e("TAG", ivMain.getScrollX() + "-");
}
});
btnScrollUp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ivMain.scrollBy(0, 10);
Log.e("TAG", ivMain.getScrollX() + "-");
}
});
btnScrollDown.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ivMain.scrollBy(0, -10);
Log.e("TAG", ivMain.getScrollX() + "-");
}
});
btnReset1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ivMain.scrollTo(0, 0);
}
});
btnReset2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ivMain.reset();
}
});
}
private void initView() {
btnScrollLeft = (Button)findViewById( R.id.btnScrollLeft );
btnScrollRight = (Button)findViewById( R.id.btnScrollRight );
btnScrollUp = (Button)findViewById( R.id.btnScrollUp );
btnScrollDown = (Button)findViewById( R.id.btnScrollDown );
btnReset1 = (Button)findViewById(R.id.btnReset1);
btnReset2 = (Button)findViewById( R.id.btnReset2 );
ivMain = (MyImageView)findViewById( R.id.ivMain );
}
}
package com.example.scrolltest;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.Scroller;
import android.widget.Toast;
import com.orhanobut.logger.Logger;
public class MyImageView extends ImageView {
// 开始坐标
private float startX;
private Scroller scroller;
// 图片宽高
private int myWidth;
// 距离左边的距离
private int slideLeft;
// 画笔
Paint paint;
// 位图
Bitmap slidingBitmap;
// 上下文
Context context;
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context=context;
scroller = new Scroller(context);
paint = new Paint();
//设置抗锯齿,就是图像边缘平滑
paint.setAntiAlias(true);
// 图片
slidingBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
// 设置位图大小
slidingBitmap = Bitmap.createScaledBitmap(slidingBitmap, 600, 600, true);
}
public void reset() {
scroller.startScroll(getScrollX(), getScrollY(), -getScrollX(), -getScrollY(), 1000);
invalidate();
}
@Override
public void computeScroll() {
if(scroller.computeScrollOffset()) {//滑动还没有完成
Log.e("TAG", "CurrX="+scroller.getCurrX());
scrollTo(scroller.getCurrX(), scroller.getCurrY());
invalidate();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
myWidth=600;
Logger.t("111").d("width>>>"+myWidth);
// 设置宽高
setMeasuredDimension(600, 600);
}
/**
* onTouchEvent
* 点击事件
* 滑动
* 触摸都在这里完成
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("111","onTouchEvent");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
Log.d("111","MotionEvent.ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
// 调用这两个方法,感受两种滑动的不同
move1(event);
// move2(event);
break;
case MotionEvent.ACTION_UP:
Log.d("111","MotionEvent.ACTION_UP");
break;
}
return true;
}
/**
* 使用scrollTo实现滑动
*/
private void move1(MotionEvent event){
Log.d("111","MotionEvent.ACTION_MOVE");
float endX = event.getX();
// 偏移量
float distanceX = endX- startX;
// 坐标
int toScrollX= (int) (getScrollX()-distanceX);
// 设置移动范围
if(toScrollX<0) {
toScrollX=0;
}else if(toScrollX>myWidth) {
toScrollX=myWidth;
}
scrollTo(toScrollX,0);
}
/**
*绘制实现滑动
*/
private void move2(MotionEvent event){
// 移动一次ACTION_MOVE要执行很多次
Logger.t("666").d("ACTION_MOVE>>>");
//2.计算结束值
float endX = event.getX();
//3.计算偏移量
float distanceX = endX - startX;
// slideLeft = (int) (slideLeft + distanceX);
slideLeft += distanceX;
//4.屏蔽非法值
Logger.t("777").d("distanceX>>>"+distanceX+"endX>>>"+endX+"startX>>>"+startX);
Logger.t("777").d("距左边的距离>>>"+slideLeft);
if(slideLeft <0){
slideLeft = 0;
}else if(slideLeft>myWidth){
slideLeft = myWidth;
}
//5.刷新,就只重新绘制
invalidate();
//6.数据还原(执行了很多周期的初始值向223.11,224.22......)
startX = event.getX();
}
/**
* 绘制
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
// 上面的滑动按钮
canvas.drawBitmap(slidingBitmap, slideLeft, 0, paint);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/btnScrollLeft"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="左移scrollBy(10, 0)" />
<Button
android:id="@+id/btnScrollRight"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="右移scrollBy(-10, 0)" />
<Button
android:id="@+id/btnScrollUp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="上移scrollBy(0, 10)" />
<Button
android:id="@+id/btnScrollDown"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="下移scrollBy(0, -10)" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/btnReset1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="瞬间复位scrollTo(0, 0)" />
<Button
android:id="@+id/btnReset2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="平滑复位View.reset()" />
</LinearLayout>
<com.example.scrolltest.MyImageView
android:id="@+id/ivMain"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#ff0000"
android:src="@drawable/ic_launcher"
/>
</LinearLayout>