在前一篇文章基础上 measure(1)LinearLayout的measure流程,我们做进一步分析并进行应用
measure调用次数
public class TestMeasureActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_measure);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/aa"
tools:context="test.onmeasure.finalss.TestMeasureActivity"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你好你好"/>
<test.onmeasure.finalss.AImageView
android:id="@+id/iamgeaaaaaa"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff0000"/>
</LinearLayout>
aa这个LinearLayout的measureVertical重复调用了3次,我也不知道为什么,我怀疑是ViewRootImpl.scheduleTraversals()调用了3次。
所以measureVertical调3次,那么AImageView的onMeasure自然就调用了6次
aa的layout_width改为fill_parent之后,aa这个LinearLayout的measureVertical依然掉了3次,所以AImageView的onMeasure调用了3次.为什么?看上篇文章把。
问题:一个view可能被多次measure,那么请问第一次的measuredWidth对第二次measure有影响吗?
没影响,meausre出来的结果只与传进来的spec值以及本view的LayoutParams宽高(三种可能,wrap_content,match_parent,正数值)有关,和measuredSize的上一次值无关。
问题
public class TestMeasureActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<TestMeasureActivity> mActivity;
public MyHandler(TestMeasureActivity activity) {
mActivity = new WeakReference<TestMeasureActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
TestMeasureActivity activity = mActivity.get();
if (activity != null) {
activity.imageView.setImageBitmap(activity.bitMap);
}
}
}
private final MyHandler mHandler = new MyHandler(this);
private AImageView imageView;
private Bitmap bitMap;
private Bitmap bitMapInNet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_measure);
TextView tw = (TextView) findViewById(R.id.text);
imageView = (AImageView) findViewById(R.id.iamgeaaaaaa);
bitMapInNet = BitmapFactory.decodeResource(getResources(), R.drawable.sky);
int width = bitMapInNet.getWidth();
int height = bitMapInNet.getHeight();
LogUtil.d("original bitmap size: "+width+"*"+height);
WorkTask task=new WorkTask() {
@Override
protected void execute() {
try {
//模拟load
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bitMap=bitMapInNet;
mHandler.sendEmptyMessage(0);
}
};
WorkThreadManager.executeTaskInBackground(task);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent"
android:layout_height="match_parent"
android:id="@+id/aa"
tools:context="test.onmeasure.finalss.TestMeasureActivity"
android:orientation="vertical">
<test.onmeasure.finalss.AImageView
android:id="@+id/iamgeaaaaaa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff0000"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你好你好"/>
</LinearLayout>
activity中有个imageview和textview,内容数据都是从网络上下载下来的,我们从服务器返回的数据一般有text的内容以及image的url地址,所以此时text是有数据的,而imageview还没有真正的图。
在子线程内加载图片然后set进去会出现问题,我们发现textview下沉了,这个效果是合理的,但是不友好.
再来看一下imageview的onMeasure调用了几次,4次,3+1,3就是和前面类似的3次,不多说了,1是由于
activity.imageView.setImageBitmap(activity.bitMap);引起的。
最好的方法就是,我们预先给他留好位置,这样textview就不用移来移去了。
但是我们一开始并没有图片,我们怎么知道给它留多大空间呢?有办法,我们跟服务端约定好,让服务端在返回url的时候把宽高也告诉我们。这样我们就知道图片宽高了。有了图片宽高,我们有2种方法给他留位置,一种是直接设置imageview的宽高,还有一种是设置measuredWidth。
方案1
public class TestMeasureActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<TestMeasureActivity> mActivity;
public MyHandler(TestMeasureActivity activity) {
mActivity = new WeakReference<TestMeasureActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
TestMeasureActivity activity = mActivity.get();
if (activity != null) {
activity.imageView.setImageBitmap(activity.bitMap);
}
}
}
private final MyHandler mHandler = new MyHandler(this);
private AImageView imageView;
private Bitmap bitMap;
private Bitmap bitMapInNet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_measure);
TextView tw = (TextView) findViewById(R.id.text);
imageView = (AImageView) findViewById(R.id.iamgeaaaaaa);
bitMapInNet = BitmapFactory.decodeResource(getResources(), R.drawable.sky);
int width = bitMapInNet.getWidth();
int height = bitMapInNet.getHeight();
LogUtil.d("original bitmap size: "+width+"*"+height);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) imageView.getLayoutParams();
lp.width=width;
lp.height=height;
imageView.setLayoutParams(lp);
WorkTask task=new WorkTask() {
@Override
protected void execute() {
try {
//模拟load
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bitMap=bitMapInNet;
mHandler.sendEmptyMessage(0);
}
};
WorkThreadManager.executeTaskInBackground(task);
}
}
简单的,不多说,主要用setLayoutParams'
方案2
public class TestMeasureActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<TestMeasureActivity> mActivity;
public MyHandler(TestMeasureActivity activity) {
mActivity = new WeakReference<TestMeasureActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
TestMeasureActivity activity = mActivity.get();
if (activity != null) {
activity.imageView.install(activity.bitMap);
}
}
}
private final MyHandler mHandler = new MyHandler(this);
private AImageView imageView;
private Bitmap bitMap;
private Bitmap bitMapInNet;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_measure);
TextView tw = (TextView) findViewById(R.id.text);
imageView = (AImageView) findViewById(R.id.iamgeaaaaaa);
bitMapInNet = BitmapFactory.decodeResource(getResources(), R.drawable.sky);
int width = bitMapInNet.getWidth();
int height = bitMapInNet.getHeight();
LogUtil.d("original bitmap size: "+width+"*"+height);
imageView.setDesiredSize(width,height);
WorkTask task=new WorkTask() {
@Override
protected void execute() {
try {
//模拟load
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bitMap=bitMapInNet;
mHandler.sendEmptyMessage(0);
}
};
WorkThreadManager.executeTaskInBackground(task);
}
}
public class AImageView extends ImageView {
public int count = 0;
public AImageView(Context context) {
super(context);
count = 0;
}
public AImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private int measuredWidthDesired;
private int measuredHeightDesired;
private boolean isDesired;
private boolean installed;
public void setDesiredSize(int wid, int height) {
measuredHeightDesired = height;
measuredWidthDesired = wid;
isDesired = true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
count++;
int mode = MeasureSpec.getMode(widthMeasureSpec);
int size = MeasureSpec.getSize(widthMeasureSpec);
LogUtil.d("call+measured width pre: " + widthMeasureSpec + " count: " + count + " size: " + size + " mode: " + mode);
if (!installed && isDesired) {
setMeasuredDimension(measuredWidthDesired, measuredHeightDesired);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
LogUtil.d("getMeasuredWidth" + getMeasuredWidth());
}
public void install(Bitmap bitMap) {
setImageBitmap(bitMap);
installed = true;
}
}
2种方法调用的onMeasure次数是一样的都是4次,但是第一种方案改变了宽高参数,由wrap_contentet变为了实际值,而第二种只是改变了某些时候的measuredWidth和measuredHeight。相对来说第二种方法更合适。
我们来看看第二种方法,什么时候会调setMeasuredDimension(measuredWidthDesired,measuredHeightDesired);呢?如果我们调了setDesiredSize,而还没有调install的话,此时就得调setMeasuredDimension(measuredWidthDesired,measuredHeightDesired)。这个时候我们知道了图片尺寸,但是没有真正把图片给设置进去。需要为图片预留空间,而等到install之后,
AImageView有图了,它的宽高就由图来支撑。
我们改下代码,把TextView改为Button,并且
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imageView. setImageBitmap (bit2);
}
});
用Button来更换图片,我们想要的ImageVie随着图片变化而变化。
第一种方式必须每次setImageBitmap时都修改一遍图片的宽高参数,而第二种方法不用。所以相对来说第二种方法优秀一点。