**内容导视:**
1. 先了解基本的api
2. 3种方式实现全屏随手滑动的自定义view
基本的API 针对于View的子类View的
view.getX( ),view.getLeft( ),view.getTranslationX( ),e.getX( ),e.getRawX( )
view.getY( ),view.getTop( ),view.getTranslationY(( ),e.getY( ),e.getRawY( )
布局:注意这里view设置了margin的
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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">
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="#34495e"
android:onClick="click"
android:text="移动view的位置"
android:textColor="#fff"/>
<com.docwei.democeshi.MyTextView
android:id="@+id/view"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginLeft="60dp"
android:layout_marginTop="50dp"
android:background="#1abc9c"
android:text="我是内容"
android:textColor="#fff"
android:textStyle="bold"/>
</RelativeLayout>
最初显示view的位置
点击移动View的位置之后:
-
mView.setTranslationX(getResources().getDimensionPixelSize(R.dimen.move_x));
mView.setTranslationY(getResources().getDimensionPixelSize(R.dimen.move_y))
- view.getX( )=view.getLeft( )+view.getTranslationX( )
- view.getY( )=view.getTop( )+view.getTranslationY( )
可以把view.getLeft( )与view.getTop( )看成一个final的常量(最初布局完成之后就不会再改变的)
想要获取移动的控件左上角坐标的位置是不能通过getLeft()或者getTop(),因为大部分情况都不是我们想要的结果。
也不能轻易的把view.getX( )或者view.getY( )来作为view左上角的坐标,要考虑margin的情况。
e.getRawX( )可以看做是按下的点在在屏幕的X坐标
e.getRawY( )可以看做是按下的点在屏幕的Y坐标,注意(包括了导航栏和actionBar的高度)
e.getX( )是按下的点相对于控件的X的位置
e.getY( )是按下的点相对于控件的Y的位置
接下来:做一个随手滑动的view(类似于launcer上图标可以拖动的效果)来验证这些值
到底有几种方式实现随手滑动的自定义view ?
实现:
方式一:通过按下的点的移动变化来计算控件(左上角坐标)的变化,这里view的移动使用view属性平移动画,传入控件左上角的坐标的上一个值和下一个值,启动动画达到效果。那关键就是获取控件左上角的坐标,从图上就可以看出。
方式二:通过按下的点的移动变化来计算控件(左上角坐标)的变化,获取view.setX( value)来做,能用setTranslationX()方法实现那一定可以通过这个setX()来实现,只是传入值的问题。
方式三:比较简单,直接获取leftMargin和topMargin确定view的位置,然后直接setLayoutParams(params)实现。
方式一:具体代码:
public class MyTextView extends TextView {
private static final String TAG = "MyTextView";
private int mScreenWidth;
private int mScreenHeight;
private final int mTouchSlop;
private int mOffsetY;
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
mScreenWidth=context.getResources().getDisplayMetrics().widthPixels;
mScreenHeight=context.getResources().getDisplayMetrics().heightPixels;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
//***************************方式一start***************************************
private int leftMargin;
private int topMargin;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(oldw!=w){
CoordinatorLayout.MarginLayoutParams params= (CoordinatorLayout.MarginLayoutParams) getLayoutParams();
leftMargin=params.leftMargin;
topMargin=params.topMargin;
}
}
int downPointX;
int downPointY;
int moveLastPointX;
int moveLastPointY;
int downX;
int downY;
int moveLastX;
int moveLastY;
boolean flag=false;
public void setOffsetY(int offsetY) {
//mOffsetY就是导航栏和actionbar的高度之和
mOffsetY = offsetY;
}
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
//根据按下点的位置获取view的左上角的坐标
downPointX = (int) (e.getRawX()-e.getX());
downPointY = (int) (e.getRawY()-e.getY());
downX= (int) e.getRawX();
downY= (int) e.getRawY();
flag=true;
break;
case MotionEvent.ACTION_MOVE:
if(flag){
moveLastPointX = downPointX;
moveLastPointY = downPointY;
moveLastX=downX;
moveLastY=downY;
flag=false;
}
int moveX = (int) e.getRawX();
int moveY = (int) e.getRawY();
int realdx=moveX-moveLastX;
int realdy=moveY-moveLastY;
if(Math.abs(realdx)<mTouchSlop&&Math.abs(realdy)<mTouchSlop){
return true;
}
//获取移动后的坐标
int movePointX=moveLastPointX +realdx;
int movePointY=moveLastPointY +realdy;
//边界值限定------start---------
if(movePointX<0){
movePointX=0;
}
if(movePointX>mScreenWidth-getMeasuredWidth()){
movePointX=mScreenWidth-getMeasuredWidth();
}
if(movePointY<0){
movePointY=0;
}
if(movePointY>mScreenHeight-getMeasuredHeight()){
movePointY=mScreenHeight-getMeasuredHeight();
}
//边界值限定------end------------
//这里使用属性动画,其实就是左上角坐标的变化,,
//最后两个参数,一个是之前的坐标,再一个就是平移之后的坐标,这个Y坐标要是相对于父布局的坐标,而不是屏幕的坐标
//Y要减去的是状态栏和导航栏的高度,
//使用属性动画还要考虑leftMargain和topMargin
ObjectAnimator animX=ObjectAnimator.ofFloat(this,"TranslationX", moveLastPointX-leftMargin, movePointX-leftMargin);
ObjectAnimator animY=ObjectAnimator.ofFloat(this,"TranslationY", moveLastPointY-mOffsetY-topMargin, movePointY-mOffsetY-topMargin);
animX.setDuration(10);
animY.setDuration(10);
animX.start();
animY.start();
moveLastPointX = movePointX;
moveLastPointY = movePointY;
moveLastX=moveX;
moveLastY=moveY;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
获取actionBar和到导航栏的高度,可以在activity的oncreate方法里面获取
private void initData() {
mView = (MyTextView) findViewById(R.id.view);
mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int StatusBarHeight= rectgle.top;
int contentViewTop=
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
int TitleBarHeight= contentViewTop - StatusBarHeight;
mView.setOffsetY(contentViewTop);
mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
方法二:
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()){
case MotionEvent.ACTION_DOWN:
//根据按下点的位置获取view的左上角的坐标
downPointX = (int) (e.getRawX()-e.getX());
downPointY = (int) (e.getRawY()-e.getY());
downX= (int) e.getRawX();
downY= (int) e.getRawY();
flag=true;
break;
case MotionEvent.ACTION_MOVE:
if(flag){
moveLastPointX = downPointX;
moveLastPointY = downPointY;
moveLastX=downX;
moveLastY=downY;
flag=false;
}
int moveX = (int) e.getRawX();
int moveY = (int) e.getRawY();
int realdx=moveX-moveLastX;
int realdy=moveY-moveLastY;
if(Math.abs(realdx)<mTouchSlop&&Math.abs(realdy)<mTouchSlop){
return true;
}
//获取移动后的坐标
int movePointX=moveLastPointX +realdx;
int movePointY=moveLastPointY +realdy;
//边界值限定------start---------
if(movePointX<0){
movePointX=0;
}
if(movePointX>mScreenWidth-getMeasuredWidth()){
movePointX=mScreenWidth-getMeasuredWidth();
}
if(movePointY<0){
movePointY=0;
}
if(movePointY>mScreenHeight-getMeasuredHeight()){
movePointY=mScreenHeight-getMeasuredHeight();
}
//边界值限定------end------------
setX(movePointX);
setY(movePointY-mOffsetY);
moveLastPointX = movePointX;
moveLastPointY = movePointY;
moveLastX=moveX;
moveLastY=moveY;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
方式三:
public class MyTextView extends TextView {
private static final String TAG = "MyTextView";
private int mScreenWidth;
private int mScreenHeight;
private final int mTouchSlop;
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
mScreenWidth=context.getResources().getDisplayMetrics().widthPixels;
mScreenHeight=context.getResources().getDisplayMetrics().heightPixels;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
int downPointX;
int downPointY;
int moveLastPointX;
int moveLastPointY;
private boolean flag=false;
@Override
public boolean onTouchEvent(MotionEvent event) {
CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) getLayoutParams();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
//根据按下点的位置
downPointX = (int) event.getRawX();
downPointY = (int) event.getRawY();
flag=true;
return true;
}
case MotionEvent.ACTION_MOVE: {
if(flag){
moveLastPointX = downPointX;
moveLastPointY = downPointY;
flag=false;
}
//获取变化的差值
int moveX = (int) event.getRawX();
int moveY = (int) event.getRawY();
int realdx=moveX-moveLastPointX;
int realdy=moveY-moveLastPointY;
//根据变化的差值来设置左上方向的margin确定控件的位置
int left = layoutParams.leftMargin +realdx;
int top = layoutParams.topMargin + realdy;
//边界值限定------start---------
if(left<0){
left=0;
}
if(left>mScreenWidth-getMeasuredWidth()){
left=mScreenWidth-getMeasuredWidth();
}
if(top<0){
top=0;
}
if(top>mScreenHeight-getMeasuredHeight()){
//有必要的话还需要考虑虚拟键的高度
top=mScreenHeight-getMeasuredHeight();
}
//边界值限定------end------------
layoutParams.leftMargin = left;
layoutParams.topMargin = top;
setLayoutParams(layoutParams);
//记住最后的位置,把这赋给上一个点
moveLastPointX=moveX;
moveLastPointY=moveY;
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return true;
}
}
代码其实都是很简单,只是用来让大家验证
demo源码地址:
http://download.youkuaiyun.com/download/doc__wei/9895669