demo效果如下图:
拖动图片时需要监听整个屏幕,识别触摸点位置,位置在图片上拖动时,就移动图片;触摸点不在图片上时,不移动图片。
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.cindy.picdrag.MainActivity">
<ImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="@+id/iv"
android:src="@drawable/yy"/>
</LinearLayout>
线性布局里放了一张图片。图片默认位置在屏幕左上角。
MainActivity.java
定义ImageView iv//布局里的ImageView
View rootView;//屏幕根布局
int actionBarHeight; //表示demo图中蓝色部分的高度
int notifiHeight;// 表示demo图中通知栏的高度
ImageView moveIv; //表示拖动需要显示的ImageView
onCreate方法:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootView = getLayoutInflater().inflate(R.layout.activity_main,null);//获取根布局
rootView.post(new Runnable() {
@Override
public void run() {
if(getSupportActionBar()!=null){
//获取actionBar的高度
actionBarHeight = getSupportActionBar().getHeight();
//获取通知栏的高度
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
notifiHeight = rect.top;
System.out.println("height:"+(actionBarHeight+notifiHeight));
}else{
System.out.println("view has not a actionBar");
}
}
});
iv = (ImageView)findViewById(R.id.iv);
}
捕获触摸点的x,y坐标
//返回触摸点的x坐标
private float getTouchXPoint(MotionEvent event,int pointIndex){
return MotionEventCompat.getX(event, pointIndex);
}
//返回触摸点的y坐标
private float getTouchYPoint(MotionEvent event,int pointIndex){
return MotionEventCompat.getY(event, pointIndex);
}
如上图,五个点从上到下命名为ABCDE.
需要判断触摸点是否包含在imageView中,如D点,此时拖动,图片会随着触摸点的移动而移动,如果触摸点是E点,则图片不动。
A点是坐标原点,上面的getTouchXPoint,getTouchYpoint坐标就是相对A点的坐标值。
actionBar和notifyBar的高度和就是AB的值。设该值为offsetHeight
获取图片初始位置:
LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)iv.getLayoutParams();
int ivWidth = iv.getWidth();
int ivHeight = iv.getHeight();
showLog("左上角x: "+llp.leftMargin+" 左上角y: "+llp.topMargin);
showLog("左下角x: "+llp.leftMargin+" 左下角y: "+(llp.topMargin+ivHeight));
showLog("右上角x: "+(llp.leftMargin+ivWidth)+"右上角y: "+llp.topMargin);
showLog("右下角x: "+(llp.leftMargin+ivWidth)+"右下角y: "+(llp.topMargin+ivHeight));
用 LinearLayout.LayoutParams获取的iv初始位置相对于B点。
这样就有一个问题:触摸点的原点是A点(0,0),而iv的原点是B点(0,243)。需要统一坐标系:触摸点的y值-offsetHeight后即可和iv的坐标系统一。
判断触摸点是否在iv范围内:
int relx = x;
int rely = (int)getTouchYPoint(event,pointIndex)-offsetHeight;
if((relx>llp.leftMargin)&& (relx<llp.leftMargin+ivWidth)&&
(rely>llp.topMargin)&&(rely<llp.topMargin+ivHeight)){
return true;
}else{
return false;
}
返回true表示触摸点在image中,返回false表示触摸点在image外。
复写onTouchEvent方法,实现拖动
@Override
public boolean onTouchEvent(MotionEvent event) {
int actionTag = MotionEventCompat.getActionMasked(event);
int pointIndex = MotionEventCompat.getActionIndex(event);
switch (actionTag){
case MotionEvent.ACTION_DOWN:
if(isPointInPic(event,pointIndex)){
moveIv = iv;
}
break;
case MotionEvent.ACTION_MOVE:
if(moveIv!=null){
LinearLayout.LayoutParams llpm = (LinearLayout.LayoutParams)iv.getLayoutParams();
llpm.leftMargin = (int)(getTouchXPoint(event,pointIndex)-difX); //左边距
llpm.topMargin = (int)(getTouchYPoint(event,pointIndex)-difY);//上边距
iv.setLayoutParams(llpm);
}
break;
case MotionEvent.ACTION_UP:
moveIv = null;
break;
}
return super.onTouchEvent(event);
}
MotionEvent.ACTION_DOWN:按下。此时需要判断触摸点是否在imageView内部。如果在内部,把iv赋值给moveIv.
MotionEvent.ACTION_MOVE:监听移动。当moveIv不为空时重置iv的位置,实现拖动。
MotionEvent.ACTION_UP:监听抬起。抬起时把moveIv置空。
int x = (int)getTouchXPoint(event,pointIndex);
int y = (int)getTouchYPoint(event,pointIndex);
//图片初始位置
LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)iv.getLayoutParams();
difX = x-llp.leftMargin;//触摸点相对imageviwe的x偏移量
difY = y-llp.topMargin;//触摸点相对imageviwe的y偏移量
difX和difY是图上点D相对于点C的x,y偏移量。
因为imageView移动的位置是相对于点C而不是点D,所以MotionEvent.ACTION_MOVE事件中,imageView的位置参数需要把偏移量减掉。
完整的MainActivity如下
public class MainActivity extends AppCompatActivity {
private ImageView iv;
private View rootView;
private int actionBarHeight = 0;
private int notifiHeight;
private ImageView moveIv;
private float difX;
private float difY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootView = getLayoutInflater().inflate(R.layout.activity_main,null);//获取根布局
rootView.post(new Runnable() {
@Override
public void run() {
if(getSupportActionBar()!=null){
//获取actionBar的高度
actionBarHeight = getSupportActionBar().getHeight();
//获取通知栏的高度
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
notifiHeight = rect.top;
System.out.println("height:"+(actionBarHeight+notifiHeight));
}else{
System.out.println("view has not a actionBar");
}
}
});
iv = (ImageView)findViewById(R.id.iv);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int actionTag = MotionEventCompat.getActionMasked(event);
int pointIndex = MotionEventCompat.getActionIndex(event);
switch (actionTag){
case MotionEvent.ACTION_DOWN:
if(isPointInPic(event,pointIndex)){
moveIv = iv;
}
break;
case MotionEvent.ACTION_MOVE:
if(moveIv!=null){
LinearLayout.LayoutParams llpm = (LinearLayout.LayoutParams)iv.getLayoutParams();
llpm.leftMargin = (int)(getTouchXPoint(event,pointIndex)-difX); //左边距
llpm.topMargin = (int)(getTouchYPoint(event,pointIndex)-difY);//上边距
iv.setLayoutParams(llpm);
}
break;
case MotionEvent.ACTION_UP:
moveIv = null;
break;
}
return super.onTouchEvent(event);
}
//返回触摸点的x坐标
private float getTouchXPoint(MotionEvent event,int pointIndex){
return MotionEventCompat.getX(event, pointIndex);
}
//返回触摸点的y坐标
private float getTouchYPoint(MotionEvent event,int pointIndex){
return MotionEventCompat.getY(event, pointIndex);
}
private boolean isPointInPic(MotionEvent event,int pointIndex){
int x = (int)getTouchXPoint(event,pointIndex);
int y = (int)getTouchYPoint(event,pointIndex);
//图片初始位置
LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)iv.getLayoutParams();
int ivWidth = iv.getWidth();
int ivHeight = iv.getHeight();
int relx = x;
int rely = (int)getTouchYPoint(event,pointIndex)-actionBarHeight-notifiHeight;
//计算D点和C点的偏移
difX = x-llp.leftMargin;
difY = y-llp.topMargin;
//判断点击的点是否在图片的范围内
if((relx>llp.leftMargin)&&(relx<llp.leftMargin+ivWidth)&&
(rely>llp.topMargin)&&(rely<llp.topMargin+ivHeight)){
return true;
}else{
return false;
}
}
}