Android Scroll 滑动效果 及 触摸事件处理
Posted 春招进大厂的梦想家
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Scroll 滑动效果 及 触摸事件处理相关的知识,希望对你有一定的参考价值。
android Scroll 滑动效果 及 触摸事件处理
跟着《安卓群英传》看的,很多知识点在书上,这里就写一些小demo就好了,以后复习
一、TouchEvent实现滑动——小球跟着手指走
1.新建一个类继承自view,并覆写onDraw()方法
package com.example.toucheventactivity.Bean;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.inputmethod.CursorAnchorInfo;
import androidx.annotation.Nullable;
public class TestView extends View {
int x, y;
public TestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public void setXY(int _x, int _y) {
x = _x;
y = _y;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.CYAN);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAntiAlias(false);
canvas.drawCircle(x, y, 40, paint);
paint.setColor(Color.WHITE);
canvas.drawCircle(x-8,y-8,8,paint);
}
}
2.把testview加载到主活动上
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<com.example.toucheventactivity.Bean.TestView
android:id="@+id/testview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
3.主活动java文件下编辑代码
package com.example.toucheventactivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.example.toucheventactivity.Bean.TestView;
public class MainActivity extends AppCompatActivity {
private TestView mTestView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTestView = findViewById(R.id.testview);
mTestView.setOnTouchListener(new mOnTouch());
}
//处理触摸事件
private class mOnTouch implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
int x1, y1;
x1 = (int) event.getX();
y1 = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTestView.setXY(x1, y1);
//执行刷新操作
mTestView.invalidate();
break;
case MotionEvent.ACTION_UP:
Toast.makeText(MainActivity.this,"您抬起了手指",Toast.LENGTH_SHORT).show();
break;
case MotionEvent.ACTION_MOVE:
mTestView.setXY(x1,y1);
mTestView.invalidate();
break;
case MotionEvent.ACTION_CANCEL:
Toast.makeText(MainActivity.this,"您取消了触碰",Toast.LENGTH_SHORT).show();
break;
}
return true;
}
}
}
4.运行效果:
二、多点触控
- Android的多点触控功能需要运行在Android2.0版本以上
- 支持多点触控,可以在屏幕上同时使用三个手指完成缩放、旋转或者任何使用多点触控想做的事情;
- 多点触控和单点触控的基本原理是一直的。当手指触控屏幕是,MotionEvent对象被创建,并且被传递到前面介绍的方法中
- 在一个手势中,可以使用getPointerId()方法获得出触碰点id,用来在后续的触摸事件中跟踪手指。发生一系列的动作后,可以使用**findPointerIndex()**方法找到触点I当前对应的触点索引,然后使用触点索引获取触摸事件的信息。
1.使用
package com.example.toucheventactivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
public class MultiActionActivity extends AppCompatActivity {
private static final String TAG = "MultiActionActivity";
private int pointerId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_multi_action);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//得出触碰点id
pointerId = event.getPointerId(0);
//找到当前触碰点的索引
int pointerIndex = event.findPointerIndex(pointerId);
//触碰点的xy坐标
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
//大于一表明有多个点进行了触发
if (event.getPointerCount() > 1) {
for (int i = 0; i < event.getPointerCount(); i++) {
pointerIndex = event.findPointerIndex(i);
x = event.getX(pointerIndex);
y = event.getY(pointerIndex);
Log.d(TAG, "MulitiTouch Event,The Point + " + i + ":" + "x:" + x + "," + y);
}
} else {
Log.d(TAG, "Single Touch Event: -> " + x + "," + y);
}
return true;
}
public static String actionToString(int action) {
switch (action) {
case MotionEvent.ACTION_DOWN:
return "Down";
case MotionEvent.ACTION_MOVE:
return "Move";
case MotionEvent.ACTION_CANCEL:
return "cancel";
case MotionEvent.ACTION_UP:
return "up";
case MotionEvent.ACTION_POINTER_UP:
return "Pointer Up";
case MotionEvent.ACTION_OUTSIDE:
return "Outside";
case MotionEvent.ACTION_POINTER_DOWN:
return "Point Down";
}
return "";
}
}
三、手势
- 手势也是一组触摸事件的序列,由基本触摸事件的动作组成。
- 手势可以是简单的触摸事件序列,例如单击、滑屏等,也可以是自定义更复杂的触摸事件序列。基本的手势包括单击、长按、滑动、拖动、双击、缩放操作。
- 每种手势都是用户的一种特定动作,触摸屏可以识别这些动作完成相应的功能。滑动就是手指在屏幕上拖动一个物体,快速地朝一个方向移动,然后抬起。
- Android提供了GestureDetector类检测的一些常见手势,其中的方法包括onDown()、 onLongPress()和onFling()等。另外,使用scaleGestureDetector类来实现缩放手势。
- 当初始化GestureDetector对象时,需要传入一个实现了OnGestureListener接口的参数,当一个特定的手势被识别时,就会执行OnGestureListener中的各种手势的处理方法。
- 为了使GestureDetector对象能够接收触摸事件,需要覆盖View或Activity的onTouchEvent()方法,并将所有的触摸事件传递到GestureDetector对象中。
- 如果只想处理部分手势,可以继承SimpleOnGestureListener。
- 缩放手势有两种操作:一种是两个手指同时触摸屏幕,向相互远离的方向移动,然后同时离开屏幕,这是放大操作;另一种是两个手指同时触摸屏幕,向相互靠近的方向移动,然后同时离开屏幕,这是缩小操作。
1.实例:缩放一个图片
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity"
android:background="#27B1B1">
<com.example.toucheventactivity.Image.ScaleImageView
android:src="@drawable/ic_launcher_foreground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
new一个类继承自AppCompatImageView,里面用来处理手势操作的主要逻辑,注释很详细了可以看着注释回忆。
package com.example.toucheventactivity.Image;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.AppCompatImageView;
@RequiresApi(api = Build.VERSION_CODES.M)
public class ScaleImageView extends AppCompatImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener {
ScaleGestureDetector mScaleGestureDetector;
Matrix mScaleMatrix = new Matrix();
//当前的缩放度
float initScale = 1.0f;
//最大的缩放是6倍
static final float SCALE_MAX = 6.0f;
float[] matrixValues = new float[9];
public ScaleImageView(@NonNull @org.jetbrains.annotations.NotNull Context context, @Nullable @org.jetbrains.annotations.Nullable AttributeSet attrs) {
super(context, attrs);
mScaleGestureDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(this);
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = getScale();
//检测到基于原来的缩放还有一个新的缩放尺度
float scaleFactor = detector.getScaleFactor();
//不能比最大的缩放还要大,比最小的还要小
if ((scale < SCALE_MAX && scaleFactor > 1.0f) || (scale > initScale && scaleFactor < 1.0f)) {
if ((scaleFactor * scale < initScale)) {
//放大倍数是初始缩放和当前缩放的一个比值
scaleFactor = initScale / scale;
}
if (scale * scaleFactor > SCALE_MAX) {
scaleFactor = SCALE_MAX / scale;
}
}
// mScaleMatrix.postScale(scaleFactor,scaleFactor,getWidth()/2,getHeight()/2);
//以手势聚焦点为中心点进行缩放
mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX() / 2, detector.getFocusY() / 2);
//检查一下边界,是否有白边之类的
checkBorderAndCenterWhenScale();
//保存图片的matrix
setImageMatrix(mScaleMatrix);
return true;
}
private void checkBorderAndCenterWhenScale() {
//得到当前图片的绘制区域
RectF rectF = getMatrixRecF();
//移动的空间
float deltaX = 0;
float deltaY = 0;
int width = getWidth();
int height = getHeight();
if (rectF.width() >= width) {
if (rectF.left > 0) {
deltaX = - rectF.left;
}
if ((rectF.right < width)) {
deltaX = width - rectF.right;
}
}
if (rectF.height() >= height) {
//说明图片与顶部有空白区域,需要向上调整
if (rectF.top > 0) {
deltaY = - rectF.top;
}
//说明与底部有空白,需要向下调整
if (rectF.bottom < height) {
deltaY = height - rectF.bottom;
}
}
//移动的具体方法
if (rectF.width()<width) {
deltaX = width*0.5f - rectF.right+0.5f*rectF.width();
}
if(rectF.height()<height){
deltaY = height*0.5f-rectF.bottom+0.5f*rectF.height();
}
mScaleMatrix.postTranslate(deltaX,deltaY);
}
private float getScale() {
mScaleMatrix.getValues(matrixValues);
return matrixValues[Matrix.MSCALE_X];
}
//得到当前绘图区域的边界
private RectF getMatrixRecF() {
Matrix matrix = mScaleMatrix;
RectF rectF = new RectF();
//开始绘制
Drawable d = Android开发学习之Scroll分析