【问题标题】:decodeSizedBitmap to avoid outOfMemory didn't workdecodeSizedBitmap 以避免 outOfMemory 不起作用
【发布时间】:2017-03-15 20:32:16
【问题描述】:

我有一个显示 8 个图像的活动,所以我不得不使用此代码来避免 outOfMemory,这在某些情况下可以正常工作。

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                     int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

这在这个布局上很好用

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:weightSum="1"
android:layout_width="match_parent"
android:padding="10dp"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.fahrul.pesantrenbahrululum.FotoPerdivisi"
android:orientation="vertical"
tools:showIn="@layout/app_bar_foto_perdivisi">
<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentTop="true"
    android:layout_alignParentStart="true">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <LinearLayout
            android:id="@+id/kiriFotoPerdivisi"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:orientation="vertical">
        </LinearLayout>
        <LinearLayout
            android:id="@+id/kananFotoPerdivisi"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:orientation="vertical">
        </LinearLayout>
    </LinearLayout>
</ScrollView>
</LinearLayout>

这里是java代码

public class FotoPerdivisi extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, ImageView.OnClickListener{
    LinearLayout kiriLay, kananLay;
    public int[] imageIds={
            R.drawable.foto_perdivisi1,R.drawable.foto_perdivisi2,R.drawable.foto_perdivisi3,R.drawable.foto_perdivisi4,
            R.drawable.foto_perdivisi5,R.drawable.foto_perdivisi6,R.drawable.foto_perdivisi7
    };
    public int[] Ids={
            R.id.img1,R.id.img2,R.id.img3,R.id.img4,R.id.img5,R.id.img6,R.id.img7
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_foto_perdivisi);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbarFotoPerdivisi);
        setSupportActionBar(toolbar);
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_foto_perdivisi);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();
        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_foto_perdivisi);
        navigationView.setNavigationItemSelectedListener(this);
        kiriLay = (LinearLayout)findViewById(R.id.kiriFotoPerdivisi);
        kananLay = (LinearLayout)findViewById(R.id.kananFotoPerdivisi);
//        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_foto_perdivisi);
//        navigationView.setNavigationItemSelectedListener(this);
//        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.foto_perdivisiGrid);
//        recyclerView.setHasFixedSize(true);
//        RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(),2);
//        recyclerView.setLayoutManager(layoutManager);
//        ArrayList<CreateList> createLists = prepareData();
//        MyAdapter adapter = new MyAdapter(getApplicationContext(),createLists);
//        recyclerView.setAdapter(adapter);
        prepareLayout();
    }
    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight
                    && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }
    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                         int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    public void prepareLayout(){
        for(int i = 0; i < imageIds.length;i++) {
            ImageView img = new ImageView(this);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (150 * getResources().getDisplayMetrics().density));
            img.setLayoutParams(params);
            img.setScaleType(ImageView.ScaleType.CENTER_CROP);
            img.setOnClickListener(this);
            img.setImageBitmap(decodeSampledBitmapFromResource(getResources(),imageIds[i],params.width/2,params.height/2));
            img.setId(Ids[i]);
            if (i % 2==0) {
                kiriLay.addView(img);
            } else{
                kananLay.addView(img);
            }
        }
    }
    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_foto_perdivisi);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_home) {
            Intent intent = new Intent(getApplicationContext(),MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        } else if (id == R.id.nav_daftar) {
            Intent intent = new Intent(getApplicationContext(),DaftarIsiActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        } else if (id == R.id.nav_foto) {
            Intent intent = new Intent(getApplicationContext(),Gallery.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        } else if (id == R.id.nav_info) {
            Intent intent = new Intent(getApplicationContext(),InfoActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        } else if (id == R.id.nav_exit) {
            System.exit(0);
        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_foto_perdivisi);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    @Override
    public void onClick(View view) {
        Intent intent = new Intent(getApplicationContext(),ImageViewer.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        ImageView img = (ImageView)view;
        for(int i = 0; i < Ids.length; i++){
            if(img.getId() == Ids[i]) {
                intent.putExtra("position", i);
            }
        }
        intent.putExtra("curUse",1);
        startActivity(intent);
    }
}

但是如果我改变线性布局参数 android:layout_width="0dp" android:layout_weight="0.5" 代码不起作用,我认为这是因为在 layoutParams 中宽度为 0,导致算术错误。 如何避免 outOfMemoryException 并将布局参数保留为 android:layout_width="0dp" android:layout_weight="0.5" ? 感谢您的任何回应

【问题讨论】:

    标签: java android


    【解决方案1】:

    我不得不使用这段代码来避免 outOfMemory 这在某些情况下可以正常工作 案例。

    我还遇到了很多关于图像缓存和加载的问题。也使用decodeSampledBitmapFromResource。但相信我,Glide/Picasso 非常简单。您只需添加 3 或 4 行,而不是使用复杂的方法,如 calculateInSampleSizedecodeSampledBitmapFromResource

    要将 GLIDE 添加到您的项目中:

    首先在app gradle中添加依赖:

    dependencies {
      compile 'com.github.bumptech.glide:glide:3.7.0'
    }
    

    然后在您的 prepareLayout() 方法中,替换以下行

    img.setImageBitmap(decodeSampledBitmapFromResource(getResources(),imageIds[i],params.width/2,params.height/2));
                img.setId(Ids[i]);
    

    与:

     Glide.with(this).load(R.id.img1).into(imageView);  //As example
    

    看看here 使用 GLIDE。

    希望这会有所帮助。

    【讨论】:

    • 谢谢,我不知道有这个外部库
    • 欢迎,很高兴它有帮助
    【解决方案2】:

    您的问题是视图尚未布局,因此没有它的最终尺寸(getHeight() 和 getWidth() 将返回 0)。
    decodeSampledBitmapFromResource 需要这些尺寸来确定它有多大应该将位图加载为。

    解决方案是在解码图像之前等待布局传递:

        // place a runnable at the end of the message queue, after the layout pass.
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                prepareLayout();
            }
        });
    

    还要确保将正确的尺寸实际传递给decodeSampledBitmapFromResource。例如不是 MATCH_PARENT 常量,而是父级的实际高度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-24
      • 2011-10-23
      • 1970-01-01
      • 2017-07-16
      • 2017-07-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多