当前位置:首页 » 安卓系统 » android获取view

android获取view

发布时间: 2023-10-24 02:53:59

❶ android 怎么在oncreate中获得view的坐标

这个问题大家肯定遇到过不止一次,其实很简单,解决它也很容易,但是咱们追求的毕竟不是解决它,而是找到几种方法去解决,并且这么解决的原理是什么。
这里列出4种解决方案:
Activity/View#onWindowFocusChanged
这个函数的含义是:view已经初始化完毕了,宽/高已经准备好了,这个时候去获取宽高是可以成功获取的。但是需要注意的是onWindowFocusChanged函数会被调用多次,当Activity的窗口得到焦点和失去焦点时均会被调用一次,如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
L.i("onWindowFocusChanged : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}123456

view.post(runnable)
通过post可以将一个runnable投递到消息队列的尾部,然后等待UI线程Looper调用此runnable的时候,view也已经初始化好了。
v_view1.post(new Runnable() {
@Override
public void run() {
L.i("post(Runnable) : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});1234567

ViewTreeObserver
使用ViewTreeObserver的众多回调可以完成这个功能,比如使用OnGlobalLayoutListener这个接口,当view树的状态发生改变或者view树内部的view的可见性发生改变时,onGlobalLayout方法将被回调,因此这是获取view的宽高一个很好的时机。需要注意的是,伴随着view树的状态改变等,onGlobalLayout会被调用多次。
v_view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
L.i("ViewTreeObserver : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});1234567

再来详细介绍一下ViewTreeObserver这个类,这个类是用来注册当view tree全局状态改变时的回调监听器,这些全局事件包括很多,比如整个view tree视图的布局,视图绘制的开始,点击事件的改变等等。还有千万不要在应用程序中实例化ViewTreeObserver对象,因为该对象仅是由视图提供的。
ViewTreeObserver类提供了几个相关函数用来添加view tree的相关监听器:
public void addOnDrawListener (ViewTreeObserver.OnDrawListener listener)
该函数为api 16版本中添加,作用是注册在该view tree将要绘制时候的回调监听器,注意该函数和相关的remove函数不能在监听器回调的onDraw()中调用。
public void (ViewTreeObserver.OnGlobalFocusChangeListener listener)
该函数用来注册在view tree焦点改变时候的回调监听器。
public void addOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener listener)
该函数用来注册在该view tree中view的全局布局属性改变或者可见性改变时候的回调监听器。
public void addOnPreDrawListener (ViewTreeObserver.OnPreDrawListener listener)
该函数用来注册当view tree将要被绘制时候(view 的 onDraw 函数之前)的回调监听器。
public void addOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener listener)
该函数用来注册当view tree滑动时候的回调监听器,比如用来监听ScrollView的滑动状态。
public void addOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener listener)
该函数用来注册当view tree的touch mode改变时的回调监听器,回调函数onTouchModeChanged (boolean isInTouchMode)中的isInTouchMode为该view tree的touch mode状态。
public void addOnWindowAttachListener (ViewTreeObserver.OnWindowAttachListener listener)
api 18添加,该函数用来注册当view tree被附加到一个window上时的回调监听器。
public void (ViewTreeObserver.OnWindowFocusChangeListener listener)
api 18添加,该函数用来注册当window中该view tree焦点改变时候的回调监听器。
而且对应每一个add方法都会有一个remove方法用来删除相应监听器。

view.measure(int widthMeasureSpec, int heightMeasureSpec)
通过手动对view进行measure来得到view的宽/高,这种情况比较复杂,这里要分情况处理,根据view的layoutparams来分:
match_parent
直接放弃,无法measure出具体的宽/高。原因很简单,根据view的measure过程,构造此种MeasureSpec需要知道parentSize,即父容器的剩余空间,而这个时候我们无法知道parentSize的大小,所以理论上不可能测量处view的大小。
wrap_content
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);123

注意到(1<<30)-1,我们知道MeasureSpec的前2位为mode,后面30位为size,所以说我们使用最大size值去匹配该最大化模式,让view自己去计算需要的大小。
具体的数值(dp/px)
这种模式下,只需要使用具体数值去measure即可,比如宽/高都是100px:
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);123

源码和结果
demo代码如下
xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/v_view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>

<View
android:id="@+id/v_view2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/ic_launcher"/>

</LinearLayout>12345678910111213141516171819

activity:
public class MainActivity extends BaseActivity{
private View v_view1;
private View v_view2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

v_view1 = findViewById(R.id.v_view1);
v_view2 = findViewById(R.id.v_view2);

L.i("normal: v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());

v_view1.post(new Runnable() {
@Override
public void run() {
L.i("post(Runnable) : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});

v_view1.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
L.i("ViewTreeObserver : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
});

int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.AT_MOST);
v_view1.measure(widthMeasureSpec, heightMeasureSpec);
L.i("measure : v_view1.getMeasuredWidth():" + v_view1.getMeasuredWidth()
+ " v_view1.getMeasuredHeight():" + v_view1.getMeasuredHeight());
v_view2.measure(widthMeasureSpec, heightMeasureSpec);
L.i("measure : v_view2.getMeasuredWidth():" + v_view2.getMeasuredWidth()
+ " v_view2.getMeasuredHeight():" + v_view2.getMeasuredHeight());

}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
L.i("onWindowFocusChanged : v_view1.getWidth():" + v_view1.getWidth()
+ " v_view1.getHeight():" + v_view1.getHeight());
}
}
041424344454647484950

log日志:
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:28): normal: v_view1.getWidth():0 v_view1.getHeight():0
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:50): measure : v_view1.getMeasuredWidth():144 v_view1.getMeasuredHeight():144
I/[PID:2659]: [TID:1] MainActivity.onCreate(line:53): measure : v_view2.getMeasuredWidth():16777215 v_view2.getMeasuredHeight():16777215
I/[PID:2659]: [TID:1] 2.onGlobalLayout(line:42): ViewTreeObserver : v_view1.getWidth():144 v_view1.getHeight():144
I/[PID:2659]: [TID:1] 1.run(line:34): post(Runnable) : v_view1.getWidth():144 v_view1.getHeight():144
I/[PID:2659]: [TID:1] MainActivity.onWindowFocusChanged(line:61): onWindowFocusChanged : v_view1.getWidth():144 v_view1.getHeight():144123456

界面:

小的为view_1,大的为view_2,从log日志中就发现有问题了:view_2视图使用measure之后计算出来的宽高是错误的,所以View类的视图使用measure计算出来的结果是不准确的,这点需要特别特别注意。

❷ Android View何时能拿到宽高的值

一个View或ViewGroup中什么什么时候能拿到宽高的值?

width 表示 View 在屏幕上可显示的区域大小;
measuredWidth 表示 View 的实际大小,包括超出屏幕范围外的尺寸;

甚至有这样的公式总结到:
getMeasuredWidth() = visible width + invisible width

getMeasuredWidth() 在执行setMeasuredDimension(一般在onMeasure方法中执行)后才有值;
getWidth()在onLayout方法执行后才有值。



Constructor : 构造方法,View初始化的时候调用,在这里是无法获取其子控件的引用的.更加无法获取宽高了.

onFinishInflate : 当布局初始化完毕后回调,在这里可以获取所有直接子View的引用,但是无法获取宽高.

onMeasure : 当测量控件宽高时回调,当调用了requestLayout()也会回调onMeasure.在这里一定可以通过getMeasuredHeight()和getMeasuredWidth()来获取控件的高和宽,但不一定可以通过getHeight()和getWidth()来获取控件宽高,因为getHeight()和getWidth()必须要等onLayout方法回调之后才能确定.

onSizeChanged : 当控件的宽高发生变化时回调,和onMeasure一样,一定可以通过getMeasuredHeight()和getMeasuredWidth()来获取控件的高和宽,因为它是在onMeasure方法执行之后和onLayout方法之前回调的.

onLayout : 当确定控件的位置时回调,当调用了requestLayout()也会回调onLayout.在这里一定可以通过getHeight()和getWidth()获取控件的宽高,同时由于onMeasure方法比onLayout方法先执行,所以在这里也可以通过getMeasuredHeight()和getMeasuredWidth()来获取控件的高和宽.

addOnGlobalLayoutListener : 当View的位置确定完后会回调改监听方法,它是紧接着onLayout方法执行而执行的,只要onLayout方法调用了,那么addOnGlobalLayoutListener的监听器就会监听到.在这里getMeasuredHeight()和getMeasuredWidth()和getHeight()和getWidth()都可以获取到宽高.

onWindowFocusChanged : 当View的焦点发送改变时回调,在这里getMeasuredHeight()和getMeasuredWidth()和getHeight()和getWidth()都可以获取到宽高.Activity也可以通过重写该方法来判断当前的焦点是否发送改变了;需要注意的是这里View获取焦点和失去焦点都会回调.



(部分内容参考于网络,如有不妥,请联系删除~)

❸ android 是怎么通过在XML中配置的控件属性得到具体的view对象

我可能表达的不是很清楚,那就拿个具体的例子来说明吧
比如说,在Activity中我们需要用到一个ProgressBar控件,我们一般先在layout下的main.xml中进行配置
Xml代码
<ProgressBar
android:id="@+id/pb1"
android:layout_width="fill_parent"
android:layout_height="20dip"
<span style="color: #ff0000;"> android:indeterminateOnly="false"</span>

android:layout_gravity="center_vertical"
android:progressDrawable="@android:drawable/progress_horizontal"
android:indeterminateDrawable="@android:drawable/progress_indeterminate_horizontal"
android:minHeight="20dip"
android:maxHeight="20dip"
/>

我们看 android:indeterminateOnly="false" 这行代码的
一般我们如果要将在代码中创建一个ProgressBar,但是不通过配置文件得到。
代码如下:
java代码
ProgressBar mProgressBar=new ProgressBar(context);

<span style="color: #ff0000;">mProgressBar.setIndeterminate(false); </span>

mProgressBar.setProgressDrawable(getResources().getDrawable
(android.R.drawable.progress_horizontal));

mProgressBar.setIndeterminateDrawable(getResources().getDrawable
(android.R.drawable.progress_indeterminate_horizontal));

本来我们是希望创建一个普通的能显示进度的横条ProgressBar
但是我们发现progressBar中的进度无法更新。
我们来看进度更新的源代码setProgress():
Java代码
@android.view.RemotableViewMethod
synchronized void setProgress(int progress, boolean fromUser) {
<span style="color: #ff0000;"> if (mIndeterminate) {
return;
}</span>

if (progress < 0) {
progress = 0;
}
if (progress > mMax) {
progress = mMax;
}
if (progress != mProgress) {
mProgress = progress;
refreshProgress(R.id.progress, mProgress, fromUser);
}
}

关键是: if (mIndeterminate) { return; }
原来mIndeterminate 的值为true的话,函数直接返回了,也就是我们不能设置progressBar的进度(mProgress)
现在我们的任务是将mIndeterminate 属性设置为false
但是我们发现 mProgressBar.setIndeterminate(false); 这行代码并没有设置mIndeterminate 属性为false
我们看ProgressBar中的源代码:

Java代码
@android.view.RemotableViewMethod
public synchronized void setIndeterminate(boolean indeterminate) {
<span style="color: #ff0000;"> if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) {</span>

mIndeterminate = indeterminate;

if (indeterminate) {
// swap between indeterminate and regular backgrounds
mCurrentDrawable = mIndeterminateDrawable;
startAnimation();
} else {
mCurrentDrawable = mProgressDrawable;
stopAnimation();
}
}
}

看这行代码: if ((!mOnlyIndeterminate || !mIndeterminate) && indeterminate != mIndeterminate) {
我们发现当mOnlyIndeterminate 和mIndeterminate 之前都为true时,我们并不能将mIndeterminate 从true改变为false

google后,有人通过反射机制将ProgressBar中的mOnlyIndeterminate 设置为false(具体请看:关于使用代码来创建ProgressBar )

我现在我就在想 既然 ProgressBar中的mOnlyIndeterminate 和mIndeterminate属性都是private,而且都不能通过get和set方法来对其进行操作,那么 android 通过在XML中配置的控件属性,是怎么被转换成真正的java类呢?

求大神解惑!

问题补充

over140 写道
你应该看一下他源码里关于这个的构造函数部分的代码,注意父类里面可能也有代码,他并不是转换成java类,只是读取从XML属性读取想要的参数。

你指的是ProgressBar类中的构造函数吗? 我之前就仔细看了这些东西
ProgressBar中构造函数源码:
Java代码
/**
* Create a new progress bar with range 0...100 and initial progress of 0.
* @param context the application environment
*/
public ProgressBar(Context context) {
this(context, null);
}

public ProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.progressBarStyle);
}

public ProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mUiThreadId = Thread.currentThread().getId();
initProgressBar();

TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, 0);

mNoInvalidate = true;

Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
if (drawable != null) {
drawable = tileify(drawable, false);
setProgressDrawable(drawable);
}

mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);

mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);
mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_maxWidth, mMaxWidth);
mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_minHeight, mMinHeight);
mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_maxHeight, mMaxHeight);

mBehavior = a.getInt(R.styleable.ProgressBar_indeterminateBehavior, mBehavior);

final int resID = a.getResourceId(
com.android.internal.R.styleable.ProgressBar_interpolator,
android.R.anim.linear_interpolator); // default to linear interpolator
if (resID > 0) {
setInterpolator(context, resID);
}

setMax(a.getInt(R.styleable.ProgressBar_max, mMax));

setProgress(a.getInt(R.styleable.ProgressBar_progress, mProgress));

setSecondaryProgress(
a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));

drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);
if (drawable != null) {
drawable = tileifyIndeterminate(drawable);
setIndeterminateDrawable(drawable);
}

mOnlyIndeterminate = a.getBoolean(
R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate);

mNoInvalidate = false;

setIndeterminate(mOnlyIndeterminate || a.getBoolean(
R.styleable.ProgressBar_indeterminate, mIndeterminate));

a.recycle();
}

看以上代码,发现ProgressBar类并没有使用其父类的构造方法, 它的三个构造方法最终都需要进入到第三个构造方法内,
再看这一句
Java代码
initProgressBar();

其中具体源码如下:
Java代码
private void initProgressBar() {
mMax = 100;
mProgress = 0;
mSecondaryProgress = 0;
mIndeterminate = false;
mOnlyIndeterminate = false;
mDuration = 4000;
mBehavior = AlphaAnimation.RESTART;
mMinWidth = 24;
mMaxWidth = 48;
mMinHeight = 24;
mMaxHeight = 48;
}

看 这两句:
Java代码
mIndeterminate = false;
mOnlyIndeterminate = false;

奇怪 在这个初始化ProgressBar的过程中,明明将mIndeterminate和mOnlyIndeterminate属性设置为false
但是我们初始化后的进度条,显示后同样是不会进度的(图如下)

也就是说 mIndeterminate属性依然是true(具体请看ProgressBar中的setProgress()方法)

继续看构造方法中的其他内容时,发现关键所在:
Java代码
mOnlyIndeterminate = a.getBoolean(
R.styleable.ProgressBar_indeterminateOnly, mOnlyIndeterminate);

mNoInvalidate = false;

setIndeterminate(mOnlyIndeterminate || a.getBoolean(
R.styleable.ProgressBar_indeterminate, mIndeterminate));

这两句将mOnlydeterminate和mIndeterminate属性都设置为true,
看到这里,发现这两句都用到 a 这个对象,a 具体是什么呢?

Java代码
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, 0);

那我们能不能通过改变传入 构造函数 的参数 来使 a 发生相应的变化,进而使mOnlyIndeterminate和mIndeterminate属性设置为 true 呢?
再往下深入,发现看的我那是一头雾水,完全找不到方向,所以只得放弃这个猜想。

到最后,我发现 如果要通过正常的java访问机制(当然排除使用反射机制),来操作mOnlyIndeterminate和mIndeterminate属性是不可能的,

你说: 他并不是转换成java类,只是读取从XML属性读取想要的参数。

我们一般是通过findViewById()来根据layout中的XML文件设置的控件属性得到 具体的View控件对象(关于转换成java类,我错了,其实我想说的是怎么得到的这个对象),
这个过程是不是也是通过反射机制来完成的?假如是的话,能给我提供些具体过程的资料呢?

❹ android 如何获取一个界面最顶层的view并处理单击事件的分发机制

android事件分发机制 就是一个触摸事件发生了,从一个窗口传递到一个视图,再传递到另外一个视图,最后被消费的过程,在android中还是比较复杂的传递流程如下:

(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。

(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。

热点内容
怎么看pppoe密码 发布:2024-11-30 08:35:35 浏览:509
sandisk16gb存储卡 发布:2024-11-30 08:34:42 浏览:953
eclipsejava反编译 发布:2024-11-30 08:34:37 浏览:899
yy静态头像源码 发布:2024-11-30 08:30:21 浏览:680
javaparseint 发布:2024-11-30 08:23:12 浏览:909
抖音密码箱保险在哪里 发布:2024-11-30 08:10:43 浏览:998
广告文学脚本格式 发布:2024-11-30 08:09:57 浏览:634
期末到了解压的方法 发布:2024-11-30 07:53:49 浏览:865
sqlce数据库 发布:2024-11-30 07:41:21 浏览:726
奇瑞5x配置如何 发布:2024-11-30 07:39:50 浏览:642