Android当中的滑动效果可以有效改善之前的点击,长按等UI体验,今天就来看一看其具体的实现,下面将会分为两个部分展开介绍,*部分介绍一下基础知识,第二部分介绍具体的几种实现方式。

1,Android中的坐标系
1.1 Android坐标系
如下图所示,Android中的坐标系是向右为X轴正方向,向下为Y轴正方向;其中(0,0)坐标对应于手机屏幕的左上角

%title插图%num

1.2 视图坐标系
如下图所示,其中(0,0)代表父布局的左上角(嵌套同理)

%title插图%num

2,触控事件
MotionEvent对于用户与界面的交互来说必不可少,当然随着语音识别等AI技术的成熟,这种依赖会降低,目前我们在做的机器人产品用户与机器人交互主要是通过ASR和TTS以及人脸识别等,界面更多的是展示功能。其中MotionEvent中定义了很多事件常量,主要如下:

MotionEvent.ACTION_DOWN
MotionEvent.ACTION_UP
MotionEvent.ACTION_MOVE
MotionEvent.ACTION_CANCEL
MotionEvent.ACTION_OUTSIDE
MotionEvent.ACTION_POINTER_DOWN
MotionEvent.ACTION_POINTER_UP
MotionEvent.ACTION_BUTTON_PRESS
MotionEvent.ACTION_HOVER_ENTER
MotionEvent.ACTION_HOVER_EXIT
MotionEvent.ACTION_HOVER_MOVE
MotionEvent.ACTION_MASK
MotionEvent.ACTION_POINTER_INDEX_MASK
MotionEvent.ACTION_POINTER_INDEX_SHIFT
MotionEvent.ACTION_BUTTON_RELEAS
等,我们只需要在获取控件的时候设置onTouchEvent(EventMotion event)即可根据event收到的事件类型来做业务处理

3,获取坐标
3.1 通过View
getLeft() //获取View的左边到其父布局左边距离

getTop() //获取View的上边到其父布局上边距离

getRight() //获取View的右到其父布局左边距离

getBottom() //获取View的底边到其父布局上边边距离

3.2 通过MotionEvent
getX() //点击事件位置距离控件自身左边的距离

getY() //点击事件位置距离控件自身上边的距离

getRawX() //点击事件位置距离控件屏幕左边的距离

getRawY() //点击事件位置距离控件屏幕上边的距离

4,示例代码和解释
4.1 代码
MainActivity代码:

package com.hfut.operationscrollpre;

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-18 15:15:13
*/
public class MainActivity extends AppCompatActivity {

private static final String TAG = “MainActivity”;
Button button;
LinearLayout linearLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_DOWN”);
Log.d(“按钮:点击坐标(相对控件本身):”, “onTouch: x,” + event.getX() + “;y,” + event.getY());
Log.d(“按钮:点击坐标(相对上层布局):”, “onTouch: Left,” + v.getLeft() + “;Top,” + v.getTop() + “;Right,” + v.getRight()
+ “;Bottom,” + v.getBottom());
Log.d(“按钮:点击坐标(相对整个屏幕):”, “onTouch: X,” + event.getRawX() + “;Y,” + event.getRawY());
break;
case MotionEvent.ACTION_UP:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_UP”);
Log.d(“按钮:点击坐标(相对控件本身):”, “onTouch: x,” + event.getX() + “;y,” + event.getY());
Log.d(“按钮:点击坐标(相对上层布局):”, “onTouch: Left,” + v.getLeft() + “;Top,” + v.getTop() + “;Right,” + v.getRight()
+ “;Bottom,” + v.getBottom());
Log.d(“按钮:点击坐标(相对整个屏幕):”, “onTouch: X,” + event.getRawX() + “;Y,” + event.getRawY());
break;
case MotionEvent.ACTION_MOVE:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_MOVE”);
break;
case MotionEvent.ACTION_CANCEL:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_CANCEL”);
break;
case MotionEvent.ACTION_OUTSIDE:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_OUTSIDE”);
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_POINTER_DOWN”);
break;
case MotionEvent.ACTION_POINTER_UP:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_POINTER_UP”);
break;
case MotionEvent.ACTION_BUTTON_PRESS:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_BUTTON_PRESS”);
break;
case MotionEvent.ACTION_BUTTON_RELEASE:
Log.d(“按钮:”, “onTouch: MotionEvent.ACTION_BUTTON_RELEASE”);
break;
default:
break;
}
return true;
}
});

linearLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(“父布局:”, “onTouch: MotionEvent.ACTION_DOWN”);
Log.d(“父布局:点击坐标(相对控件本身):”, “onTouch: x,” + event.getX() + “;y,” + event.getY());
Log.d(“父布局:点击坐标(相对上层布局):”, “onTouch: Left,” + v.getLeft() + “;Top,” + v.getTop() + “;Right,” + v.getRight()
+ “;Bottom,” + v.getBottom());
Log.d(“父布局:点击坐标(相对整个屏幕):”, “onTouch: X,” + event.getRawX() + “;Y,” + event.getRawY());
break;
case MotionEvent.ACTION_UP:
Log.d(“父布局:”, “onTouch: MotionEvent.ACTION_UP”);
Log.d(“父布局:点击坐标(相对控件本身):”, “onTouch: x,” + event.getX() + “;y,” + event.getY());
Log.d(“父布局:点击坐标(相对上层布局):”, “onTouch: Left,” + v.getLeft() + “;Top,” + v.getTop() + “;Right,” + v.getRight()
+ “;Bottom,” + v.getBottom());
Log.d(“父布局:点击坐标(相对整个屏幕):”, “onTouch: X,” + event.getRawX() + “;Y,” + event.getRawY());
break;
case MotionEvent.ACTION_MOVE:
Log.d(“父布局:”, “onTouch: MotionEvent.ACTION_MOVE”);
break;
case MotionEvent.ACTION_CANCEL:
Log.d(“父布局:”, “onTouch: MotionEvent.ACTION_CANCEL”);
break;
case MotionEvent.ACTION_OUTSIDE:
Log.d(“父布局:”, “onTouch: MotionEvent.ACTION_OUTSIDE”);
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.d(“父布局:”, “onTouch: MotionEvent.ACTION_POINTER_DOWN”);
break;
case MotionEvent.ACTION_POINTER_UP:
Log.d(“父布局:”, “onTouch: MotionEvent.ACTION_POINTER_UP”);
break;
default:
break;
}
return true;
}
});
}
}
activity_main.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.operationscrollpre.MainActivity”>

<LinearLayout
android:layout_marginLeft=”100px”
android:id=”@+id/test_layout”
android:orientation=”vertical”
android:gravity=”center”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>

<Button
android:text=”我是测试按钮”
android:id=”@+id/test_button”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />
</LinearLayout>

</LinearLayout>
4.2 测试结果
(1)点击屏幕

%title插图%num

 

(2)滑动屏幕

%title插图%num

 

(3)点击按钮

%title插图%num

(4)滑动按钮

%title插图%num

分析:

getX() + getLeft()+父布局到屏幕左侧距离=getRawX()

getY()+getTop()+父布局到屏幕上边距离=getRawY()

从日志来看我们的父布局(id为test_layout)到屏幕上边距离是118px,实际上我们并没有设置marginTop属性值,这里我们千万别忘了其父布局上面还有一个存放ActionBar的FrameLayout所占据的空间,还有就是我们平时喜欢使用dp单位来表示margin属性值,而这里获取的坐标单位都是px,所以在测试的时候需要注意,有时候我们设置了margin值为100dp,显示的结果大小很可能不是100px,这之间有一个单位换算。可以使用:

public static int dp2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
获取,首先获取像素密度,然后在换算成对应的像素值;假如我们把上面的设置改为:

<LinearLayout
android:layout_marginLeft=”100dp”
android:id=”@+id/test_layout”
android:orientation=”vertical”
android:gravity=”center”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>

<Button
android:text=”我是测试按钮”
android:id=”@+id/test_button”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content” />
</LinearLayout>
再次点击子布局,日志如下:

%title插图%num

再看看MainActivity中打印的日志:

Log.d(TAG, “onCreate: “+dp2px(getApplicationContext(),100));//代码

08-18 10:39:58.096 9114-9114/? D/MainActivity: onCreate: 133 //日志
————————————————