10

StackOverflow や他の場所でこれに似たさまざまな問題を見てきましたが、これらの解決策のどれも私の問題を解決していないようです。それは次のとおりです。

シンプルな PagerAdapter を備えた ViewPager があります。各「ページ」には、少なくとも 1 つの TextView、1 つの ImageView、場合によっては別の TextView を含む XML レイアウトが関連付けられています。ImageViews に関連付けられた画像は、通常、1 つの次元が 1000 ピクセルで、もう 1 つの次元はそれ以下ですが、8 ビットのインデックス付き gif であるため、いずれも 20kB をはるかに超えるものはありません。それらを Drawables として設定し、割り当てられたスペースに合わせて適切に縮小します。

6ページしかありません。アクティビティを起動すると、正常に動作し、最後のページにめくることもできますが、右にめくってから左に戻すと、OutOfMemoryError が発生します。ViewPager は 1 画面以上離れたページを破棄することになっていて、他のさまざまな場所で提案されているように destroyItem() を処理しているため、これは私には意味がありません。

PagerAdapter のコードは次のとおりです。

package doop.doop.dedoop;

import doop.doop.dedoop.R.layout;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class HelpPagerAdapter extends PagerAdapter{
    private Context applicationContext;
    private Context activityContext;

    public HelpPagerAdapter(Context appContext, Context activContext){
        applicationContext = appContext;
        activityContext = activContext;
    }

    public int getCount() {
        return 6;
    }

    public Object instantiateItem(ViewGroup collection, int position) {
        LayoutInflater inflater = (LayoutInflater) collection.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        int resId = 0;
        int ivId = 0;
        int drawId = 0;
        switch (position) {
        case 0:
            resId = R.layout.help1;
            ivId = R.id.helpImage1;
            drawId = R.drawable.left_col_highlight;
            break;
        case 1:
            resId = R.layout.help2;
            ivId = R.id.helpImage2;
            drawId = R.drawable.right_col_highlight;
            break;
        case 2:
            resId = R.layout.help3;
            ivId = R.id.helpImage3;
            drawId = R.drawable.p2win;
            break;
        case 3:
            resId = R.layout.help4;
            ivId = R.id.helpImage4;
            drawId = R.drawable.first_move;
            break;
        case 4:
            resId = R.layout.help5;
            ivId = R.id.helpImage5;
            drawId = R.drawable.screen_bottom;
            break;
        case 5:
            resId = R.layout.help6;
            ivId = R.id.helpImage6;
            drawId = R.drawable.illegal_move;
            break;
        }
        View view = inflater.inflate(resId, null);
        collection.addView(view, 0);

        if (ivId != 0 && drawId != 0) {
            ImageView iv = (ImageView) view.findViewById(ivId);
            iv.setImageDrawable(activityContext.getResources().getDrawable(drawId));
        }

        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View)object);   
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == ((View) arg1);
    }

}

ページ レイアウトの 1 つの例を次に示します。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        style="@style/helpPageLayout" >

    <TextView
        style="@style/helpText"
        android:text="@string/help2" />

    <ImageView android:id="@+id/helpImage2"
        style="@style/helpImage" />            

</LinearLayout>

すべてが含まれているアクティビティは次のとおりです。

package doop.doop.dedoop;

import android.app.Activity;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;

public class Help extends Activity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_help);

        // Set up the ViewPager and its kin
        HelpPagerAdapter adapter = new HelpPagerAdapter(getApplicationContext(), this);
        ViewPager helpPager = (ViewPager) findViewById(R.id.helpPager);
        helpPager.setAdapter(adapter);      

    }

    public void exitHelp(View view) {
        finish();
    }

}

そのアクティビティのレイアウトは次のとおりです。

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mainHelp"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.view.ViewPager android:id="@+id/helpPager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <Button
        android:text="@string/helpBack"
        android:onClick="exitHelp"
        style="@style/mainMenuButtons" />

</LinearLayout>

最後に、エラーは次のとおりです。

12-08 11:55:23.114: E/AndroidRuntime(9090): FATAL EXCEPTION: main
12-08 11:55:23.114: E/AndroidRuntime(9090): java.lang.OutOfMemoryError
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.Bitmap.nativeCreate(Native Method)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:618)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:593)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:445)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:775)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.content.res.Resources.loadDrawable(Resources.java:1968)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.content.res.Resources.getDrawable(Resources.java:677)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at doop.doop.dedoop.HelpPagerAdapter.instantiateItem(HelpPagerAdapter.java:69)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:801)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.support.v4.view.ViewPager.populate(ViewPager.java:962)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.support.v4.view.ViewPager.populate(ViewPager.java:881)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.support.v4.view.ViewPager$3.run(ViewPager.java:237)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.os.Handler.handleCallback(Handler.java:605)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.os.Handler.dispatchMessage(Handler.java:92)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.os.Looper.loop(Looper.java:137)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at android.app.ActivityThread.main(ActivityThread.java:4514)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at java.lang.reflect.Method.invokeNative(Native Method)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at java.lang.reflect.Method.invoke(Method.java:511)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
12-08 11:55:23.114: E/AndroidRuntime(9090):     at dalvik.system.NativeStart.main(Native Method)

これは、私が現在テストしている 2 つのデバイス (Samsung Galaxy SIII で、Kindle Fire HD 8.9 ではない) のうちの 1 つだけで発生することに注意してください。

4

3 に答える 3

5

以下のことを試してみてください。

  • PNG を画像として使用し、いくつかの png-crunch-app を試してください (または、Photoshop > export for web を使用します)。
  • ビューをリサイクルします。

コンテナーから削除したビューは、ArrayList の destroyItem() で保持します。instantiateItem() の実行中、最初に配列リストに未使用のビューがあるかどうかを確認します。ある場合は、それを使用します。そうでない場合は、1 つを膨らませます。このようにして、3 つのビューだけを膨張させ、残りをリサイクルします。findViewById() の呼び出しが多すぎるのを防ぐための ViewHolders の詳細については、こちらを確認してください。

  • ビットマップをより効率的にロードします。

BitmapFactory.Options を inJustDecodeBounds = true で使用して、ビットマップをデコードし、ビットマップの境界を確認します。次に、inSampleSize を使用して小さいバージョンをデコードします。これがあなたに関連する場合 詳細については、ここを確認してください。

  • 一次アイテム

pagerAdapter のsetPrimaryItem()を利用します。そこにビットマップをロードします。この方法では、現在表示されているアイテムのビットマップのみをロードするため、メモリの負荷が軽減されます。ただし、前/次のページをプリロードしてスクロールをスムーズにし、ユーザーエクスペリエンスを向上させるという点を少し無効にします。

サンプルコード (未テスト!)

private ImageView primaryView;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object)
{
    super.setPrimaryItem(container, position, object);
    if (this.primaryView == object)
    {
        //this method might be called a lot, so don't do anything unless the primaryView changes!
        return ;
    }
    if (this.primaryView != null)
    {
        //recycle the previous bitmap to clear memory
        ((BitmapDrawable)this.primaryView.getDrawable()).getBitmap().recycle();
    }
    //set new primary view
    this.primaryView = (ImageView) object;
    int resourceId = 0;//get image resource id here
    //load bitmap here efficiently
    this.primaryView.setImageResource(resourceId);
}
于 2013-01-25T13:42:42.730 に答える
2
helpPager.setOffscreenPageLimit(MAX_PAGES);

ページがこの範囲を超えると破棄されます。

于 2012-12-12T10:36:24.497 に答える