measure(2)图片加载前预留位置

本文探讨了在Android应用中优化图片加载过程及布局调整的方法,通过两种策略实现图片预设尺寸与实际尺寸匹配,从而避免文本组件位置变动的问题。包括使用线程异步加载图片、预设图片尺寸、布局参数调整等技巧,确保应用界面在加载过程中保持稳定性和用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前一篇文章基础上 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时都修改一遍图片的宽高参数,而第二种方法不用。所以相对来说第二种方法优秀一点。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值