您的位置:首页 > 移动开发 > Android开发

Android自定义控件热身——View的坐标位置和大小详解

2017-11-06 19:51 555 查看


Android自定义控件热身——View的坐标位置和大小详解

原创 2017年02月10日
11:20:47

标签:
Android /
View坐标 /
控件大小 /
控件测量 /
坐标位置

1045

转载请注明出处:http://blog.csdn.net/xiaohao0724/article/details/54965579

在自定义控件中我们经常会用到View位置的腾挪移动,今天就来和大家一块揭开View坐标位置的神秘面纱。

android中View的坐标系统 :屏幕的左上角View绘制区是坐标系统原点(0,0),原点向右延伸是X轴正方向,原点向下延伸是Y轴正方向。



屏幕高度=状态栏高度+应用区高度=状态栏高度+(标题栏高度+View绘制区域高度)


一、View的坐标位置


1、getTop(),getBottom(),getLeft()和getRight()

需要注意view的坐标位置是相对父容器(紧包括着View的父容器不是最外层的父容器)而言的。

以getTop为例,函数源代码为:

[java] view
plain copy

/**

* Top position of this view relative to its parent.

* 相对应父控件的top位置,单位为像素,即头部到父控件的距离

* @return The top of this view, in pixels.

*/

@ViewDebug.CapturedViewProperty

public final int getTop() {

return mTop;

}



①为View.getLeft() ;

②为View.getTop();

③为ViewGroup.getRight();

④为ViewGroup.getBottom()


2、getX和getY

如果对View进行了移动如:

View.setTranslationX(200); 则View.getLeft()的值依然为①,View.getLeft() ≠ ① + 200;

View.getLeft()是控件原始位置距离父View左边的距离,那么移动后的距离如何获得呢?其实可以通过View.getX获得控件移动后的坐标View.getX = ①
+ 200即, View.getX()= View.getLeft() + View.getTranslationX();

getTop()、getBottom()、getLeft()和getRight()是控件初始位置距离父View容器上、下、左、右边的距离;

View.getX、View.getY是控件最后视觉位置(如果有移动则是移动过后的位置)距离父View父容器左边、上边的距离。

也可获取控件距离屏幕的距离

[java] view
plain copy

final int[] location = new int[2];

View.getLocationOnScreen(location);

int x = location[0];

int y = location[1];

// 控件最终视觉位置(如果有移动则是移动过后的位置)距离手机屏幕屏幕左边、上边的距离

Log.i(TAG, "OnScreenX=" + x);

Log.i(TAG, "OnScreenY=" + y);

在Activity的onCreat方法中获取不到控件的位置和大小,必修要控件完成绘制才可以获取到其位置和大小,可以用以下两种方式来实现:

[java] view
plain copy

@Override

public void onWindowFocusChanged(boolean hasFocus) {

super.onWindowFocusChanged(hasFocus);

//获取控件的位置和大小

}

[java] view
plain copy

View.getViewTreeObserver().addOnGlobalLayoutListener(

new ViewTreeObserver.OnGlobalLayoutListener() {

// 在控件完成绘制后调用

@Override

public void onGlobalLayout() {

//后去控件的位置和大小

.

.

.

// 测量成功后移除监听器,只调用一次

View.getViewTreeObserver().removeGlobalOnLayoutListener(this);

}

});


3、MotionEvent类中 getRawX()和 getRawY()

[java] view
plain copy

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

float rawX = event.getRawX();

float x = event.getX();

float rawY = event.getRawY();

float y = event.getY();

break;

case MotionEvent.ACTION_MOVE:

break;

case MotionEvent.ACTION_UP:

break;

default:

break;

}

return super.onTouchEvent(event);

}



event.getRowX():触摸点相对于手机屏幕原点的x坐标,不管App是否有状态栏、全屏等。

event.getX(): 触摸点相对于自身组件原点的x坐标 ,触摸点到触摸点所在控件左上角的X轴距离


4、通过对View多种方式的移动来研究View的getLeft和getX的不同

activity_main.xml

[html] view
plain copy

<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"

tools:context="com.havorld.viewxy.MainActivity" >

<LinearLayout

android:id="@+id/ll"

android:layout_width="match_parent"

android:layout_height="match_parent" >

<TextView

android:id="@+id/tv"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginLeft="10dp"

android:background="@android:color/holo_green_light"

android:text="我是TextView" />

</LinearLayout>

</RelativeLayout>

MainActivity.java

[java] view
plain copy

public class MainActivity extends Activity implements OnClickListener {

protected static final String TAG = "Havorld";

private TextView textView;

private LinearLayout linearLayout;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

textView = (TextView) findViewById(R.id.tv);

linearLayout = (LinearLayout) findViewById(R.id.ll);

textView.setOnClickListener(this);

linearLayout.setOnClickListener(this);

}

private void move6() {

textView.layout(textView.getLeft() + 20, textView.getTop(),

textView.getRight(), textView.getBottom());

}

private void move5() {

ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) textView

.getLayoutParams();

lp.leftMargin += 100;

textView.setLayoutParams(lp);

}

private void move4() {

linearLayout.scrollBy(-30, -30);

}

private void move3() {

textView.setTranslationY(300);

}

private void move2() {

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView,

"translationY", 0, 50, 100, 150, 200, 250, 300);

objectAnimator.setDuration(2000).start();

}

private void move1() {

TranslateAnimation translateAnimation = new TranslateAnimation(20, 20,

0, 300);

translateAnimation.setDuration(2000);

translateAnimation.setFillAfter(true);// 保持移动后的状态

textView.startAnimation(translateAnimation);

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.tv:// 点击查看位置

Log.e(TAG, "getLeft:" + textView.getLeft());

Log.e(TAG, "getTop:" + textView.getTop());

Log.e(TAG, "getX:" + textView.getX());

Log.e(TAG, "getY:" + textView.getY());

break;

case R.id.ll: // 点击对textView进行移动

// 方式一:通过补间动画移动

// move1();

// 方式二:通过属性动画移动

// move2();

// 方式三:通过setTranslationX或setTranslationY等移动

// move3();

// 方式四:通过scrollBy或ScrollTo移动

move4();

// 方式五:通过改变布局参数

// move5();

// 方式六:通过改变布局参数

move6();

break;

default:

break;

}

}

}

先点击外层布局linearLayout分别通过四种方式对textView进行移动,然后再分别点击textView获取其移动后的位置。

方式一:通过补间动画移动



注意:补间动画移动后点击TextView获取其位置时要点击其原始位置而不是移动后的位置

移动后位置的点击事件不可执行

方式二:通过属性动画移动



移动后位置的点击事件可执行

方式三:通过setTranslationX或setTranslationY等移动



移动后位置的点击事件可执行

方式四:通过ScrollTo和scrollBy移动



移动后位置的点击事件可执行

更多关于scrollTo和scrollBy的内容请参考:http://blog.csdn.net/xiaohao0724/article/details/54984850

方式五:通过改变布局参数



移动后位置的点击事件可执行

方式六:通过改变布局参数



移动后位置的点击事件可执行


二、View控件的大小


1、getWidth()和getHeight()

getWidth()和getHeight()是获取控件的宽高。得到的是view在父容器中布局好后的宽高值,如果没有父布局,那麼默认的父布局是整个屏幕。


2、getMeasuredWidth()和getMeasuredHeight()

getMeasuredWidth()和getMeasuredHeight()是获取控件内容的宽高。

对控件上的内容进行测量后得到的view里面的内容占据的宽度和高度,前提是你必须在父布局的onLayout()方法或者此View的onDraw()方法里调用measure(int widthMeasureSpec, int heightMeasureSpec),获取之前必须调用否则将与getWidth()和getHeight()得到的结果相同。

[java] view
plain copy

public class MainActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

MyLayout myLayout = new MyLayout(this);

MyTextView textView = new MyTextView(this);

LayoutParams layoutParams = new LayoutParams(200, 400);

textView.setLayoutParams(layoutParams);

myLayout.addView(textView);

setContentView(myLayout);

}

public class MyLayout extends LinearLayout {

public MyLayout(Context context) {

super(context);

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

super.onLayout(changed, l, t, r, b);

// View childView=getChildAt(0);

// childView.measure(0, 0);

// 如果添加测量的话:打印日志:MyLayout---MeasuredWidth= 200,MeasuredHeight= 400

// measure(0, 0);

// 打印日志: MyLayout---width= 1080,height= 1716

Log.i("TAG", "MyLayout---width= " + getWidth() + ",height= "

+ getHeight());

// 打印日志:MyLayout---MeasuredWidth= 1080,MeasuredHeight= 1716

Log.i("TAG", "MyLayout---MeasuredWidth= " + getMeasuredWidth()

+ ",MeasuredHeight= " + getMeasuredHeight());

}

}

public class MyTextView extends TextView {

public MyTextView(Context context) {

super(context);

setText("test test ");

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

measure(0, 0); // 添加测量方法

// 打印日志:TextView---width: 200,height: 400

Log.i("TAG", "TextView---width: " + getWidth() + ",height: "

+ getHeight());

// 打印日志:TextViewTest---MeasuredWidth: 164,MeasuredHeight: 57

Log.i("TAG", "TextView---MeasuredWidth: " + getMeasuredWidth()

+ ",MeasuredHeight: " + getMeasuredHeight());

}

}

}

注:

getMeasuredWidth()和getMeasuredHeight 的值是在 onMeausre 方法结束后获取到的;

getWidth() 和 getHeight() 的值是在 onLayout 方法结束后可以获取到的。

另外getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,

而getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。

视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,而开发人员可以在XML文件中指定视图的大小,然后视图本身会决定其最终的大小。

点击下载源码

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文已收录于以下专栏:
Android自定义控件从入门到精通
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: