今天分享一个简单的Demo。Demo实现的功能就是,用鼠标点中button的时候,然后拖动Button。这时候Button会根据你鼠标的移动而移动,同时,你鼠标点中的Button的位置也不会改变。比如你点在Button的左上角,那移动的时候。鼠标还是在Button的左上角
一言不合上效果图
大家不要介意上面那么模糊的gif图,毕竟我是用手机拍的。(介意你又能拿我怎么办。哈哈)
我们先来打个预防针,先学习基本的知识点:
涉及到的方法一共有下面几个:
view获取自身坐标:getLeft(),getTop(),getRight(),getBottom()
view获取自身宽高:getHeight(),getWidth()
motionEvent获取坐标:getX(),getY(),getRawX(),getRawY()
1.view获取自身坐标
如上图所以,绿色区域的父视图是黄色区域,所以left是55,top是55。
黄色区域的父视图是蓝色区域,所以left是60,top是115。
2.view获取自身宽高
没错。从字面意思看就能理解,就是获取View的宽高。
这里提到一个以前遇到的一个问题,就是在Activity中有时候获取某个View的width和height会为0。
3.motionEvent获取坐标
getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离
getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离
getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离
getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离
所以当我们用鼠标点击Button中间时候,那这时候getX()就是我们鼠标点击的位置与Button左边边界的距离。getY()就是点击的位置与Button顶边边界的距离。
其实设置Button跟着鼠标滑动,很简单,就是在鼠标滑动的时候,重新设置Button的x和y坐标。即使用setX()和setY()。这时候就有问题了。那二个方法中该填入的值是多少呢。让我们画个图来看下就知道了。
首先我们比如对一个Button设置setX(200),setY(200),这时候是如下图所示这样:
所以实际上对一个Button设置setX(m),setY(n),实际上是这个Button的左上角的坐标为(m,n)。所以我们在拖动的时候不能简单的把我们点击的X和Y坐标传过去。
如上图所示,假如我们点中红色的区域,来准备移动这个Button,并且鼠标移动了绿色区域那个地方,那么这个Button也会移到图上所示那样。这样才是我们所期望的样子。
但是如果单纯把绿色区域的X和Y坐标传过去,让Button来进行setX和setY 。则会出现如下那个Button所示位置。所以发现比我们期望的位置更靠右边及下边了。
这时候我们发现多的位置正好是绿色区域在这个Button内部中相对位置的X和Y坐标。
这下我们是不是就想到,对Button设置setX(getRawX()-getX())和setY(getRawY()- getY()),如果这时候你已经这么想到了。恭喜你,你已经距离最后的成功差一小步了。当你高兴的这么写后,你会发现你移动后的Button总是在鼠标点击的下方。你会发现。X轴的的确已经正确了。但是Y轴还是错误。如下图所示:
这时候你一定会问,WHY???
原来这么分析是没问题的。But这个我们前面的假设都是在这个坐标系中,但是这个坐标系的位置在哪里???
错误原因:
因为我们调用的getRawY()方法获取到的是屏幕左上角到我们点的区域的Y轴的距离,也就是以蓝色坐标系来做参考。而我们对Button设置setY()方法的时候是绿色区域的左上角到我们点的区域的Y轴距离,也就是以红色坐标系来做参考。所以我们知道了。我们在Y轴上还要减去状态栏的高度及应用标题栏的高度才可以。
那么又有新的问题了。如何获取状态栏的高度,和应用标题栏的高度:
获取状态栏高度
int
statusBarHeight = -1;
//获取status_bar_height资源的ID
int
resourceId = getResources().getIdentifier(
"status_bar_height"
,
"dimen"
,
"android"
);
if (resourceId > 0) {
//根据资源ID获取响应的尺寸值
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
获取标题栏高度
// 获取标题栏高度
Window window = getWindow();
int
contentViewTop = getWindow()
.findViewById(Window.ID_ANDROID_CONTENT).getTop();
// statusBarHeight是上面所求的状态栏的高度
titleBarHeight = contentViewTop - statusBarHeight;
所以最后我们再拖动Button的时候,会对它setX(getRawX()-getX())及setY(getRawY()-getY()-状态栏高度-标题栏高度)。其中getX()和getY()是在你点击下去的时候就获取的。也就是在motionEvent.getAction() == MotionEvent.ACTION_DOWN的时候去获取这二个值即可。因为在motionEvent.getAction() == MotionEvent.ACTION_MOVE的时候去获取getX()和getY()可能因为你拖动的速度原因造成值不同,比如你拖动很快,鼠标先过去了。Button后面才跟随着过来。这时候的getX()及getY()都不同。
既然点击按钮后可以拖动Button,那肯定对Button设置了OnTouch监听。直接上关键代码:
package yunyuan.androiddemo.coordinatelayout;
import android.app.Activity;
import android.os.Bundle;
import android.
view
.MotionEvent;
import android.
view
.
View
;
import android.
view
.Window;
import android.widget.Button;
import butterknife.BindView;
import butterknife.ButterKnife;
import yunyuan.androiddemo.R;
* Created
by
willy
on
16/12/19.
public
class Act_CoordinateLayout extends Activity{
@BindView(R.id.btn)
Button btn;
float
dx,dy;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_coordinatelayout);
ButterKnife.bind(this);
btn.setOnTouchListener(new Button.OnTouchListener() {
@Override
public
boolean onTouch(
View
view
, MotionEvent motionEvent) {
if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
dx = motionEvent.getX();
dy = motionEvent.getY();
}
else
if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
view
.setX(motionEvent.getRawX() - dx);
view
.setY(motionEvent.getRawY()- dy - getStatusBarHeight() - getTitleBarHeight());
return
true
;
public
int
getStatusBarHeight(){
int
result = 0;
int
resourceId = getResources().getIdentifier(
"status_bar_height"
,
"dimen"
,
"android"
);
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
return
result;
public
int
getTitleBarHeight(){
Window window = getWindow();
int
contentViewTop = getWindow()
.findViewById(Window.ID_ANDROID_CONTENT).getTop();
// statusBarHeight是上面所求的状态栏的高度
int
titleBarHeight = contentViewTop - getStatusBarHeight();
return
titleBarHeight;
Android体系课学习 之 网络请求库Retrofit源码分析-看这一篇就够了
- 网络请求在我们开发中起的很大比重,有一个好的网络框架可以节省我们的开发工作量,也可以避免一些在开发中不该出现的bug
- *Retrofit*是一个轻量级框架,基于*OkHttp*的一个*Restful*框架
Android体系课学习 之 网络请求库Retrofit使用方式(附Demo)
- 网络请求在我们开发中起的很大比重,有一个好的网络框架可以节省我们的开发工作量,也可以避免一些在开发中不该出现的bug
- Retrofit是一个轻量级框架,基于OkHttp的一个Restful框架
android学习之——Gradle sync failed: Could not determine artifacts for xxxxx
android学习之——Gradle sync failed: Could not determine artifacts for xxxxx
❤️【Android精进之路-02】安装Android Studio,认识Android SDK,一步步学习❤️
上一篇文章定好了Android学习计划,这篇文章就正式进入Android的学习之旅了。本文将重点介绍Android SDK的目录结构,如何安装Android Studio以及如何用Android Studio进行第一个Android应用的开发。
Re:从零开始的安卓数据存储学习生活(开篇)
生命不止,学习不息,相信数据库在掘友们那早已不是个陌生的字眼,但它的确在每个领域都绽放着自己与众不同的形式,而我们安卓开发也有着不一样的数据库存储方式,学习它就宛如迈进一个新世界大门,今天就让我们来了解这个异世界的奇妙之处。
Ability是应用所具备“能力”的抽象,也是应用程序的重要组成部分。
一个应用可以具备多种能力(即可以包含多个Ability),HarmonyOS支持应用以Ability为单位进行部署。Ability可以分为FA(Feature Ability)和PA(Particle Ability)两种类型,每种类型为开发者提供了不同的模板,以便实现不同的业务功能。
面向Android开发者的Dart学习教程
近日Google发布了Flutter2.0, 使用Flutter开发的App可以在不做修改的情况下发布到更多的主流平台;再加上早些时候Fuchsia也宣布将Dart作为主要的UI开发语言,如果未来你想