NativeExpressAdView java.lang.IndexOutOfBoundsException:索引0无效,大小为0

时间:2017-05-29 10:47:05

标签: android-recyclerview admob

我正在研究Android应用程序,它使用RecyclerView和Retrofit获取数据以解析JSON Url。我有关于此Github

的以下教程

我在Android Studio上的项目没有错误,我可以运行应用程序。但是当我打开MainActivity时它会崩溃。

我得到的错误

java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0

以下行是在setUpAndLoadNativeExpressAds

final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);

这是MainActivity

    public class MainActivity extends AppCompatActivity{

    public static final int ITEMS_PER_AD = 3;
    private static final int NATIVE_EXPRESS_AD_HEIGHT = 150;
    private static final String AD_UNIT_ID = ADMOB_NATIVE_MENU_ID;

    private StartAppAd startAppAd = new StartAppAd(this);
    private RecyclerView mRecyclerView;
    private List<Object> mRecyclerViewItems = new ArrayList<>();
    private KontenAdapter kontenAdapter;


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


        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);

        // Use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView.
        mRecyclerView.setHasFixedSize(true);

        // Specify a linear layout manager.
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(layoutManager);

//        mRecyclerViewItems = new ArrayList<>();

        // Update the RecyclerView item's list with menu items and Native Express ads.
//        addMenuItemsFromJson();
        loadJSON();
//        initData();
        setUpAndLoadNativeExpressAds();

    }



    /**
     * Adds Native Express ads to the items list.
     */
    private void addNativeExpressAds() {

        // Loop through the items array and place a new Native Express ad in every ith position in
        // the items List.
        for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
            final NativeExpressAdView adView = new NativeExpressAdView(MainActivity.this);
            mRecyclerViewItems.add(i, adView);
        }
    }

    /**
     * Sets up and loads the Native Express ads.
     */
    private void setUpAndLoadNativeExpressAds() {
        mRecyclerView.post(new Runnable() {
            @Override
            public void run() {
                final float scale = MainActivity.this.getResources().getDisplayMetrics().density;
                // Set the ad size and ad unit ID for each Native Express ad in the items list.
                for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
                    final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);
                    final CardView cardView = (CardView) findViewById(R.id.ad_card_view);
                    final int adWidth = cardView.getWidth() - cardView.getPaddingLeft()
                            - cardView.getPaddingRight();
                    AdSize adSize = new AdSize((int) (adWidth / scale), NATIVE_EXPRESS_AD_HEIGHT);
                    adView.setAdSize(adSize);
                    adView.setAdUnitId(AD_UNIT_ID);
                }

                // Load the first Native Express ad in the items list.
                loadNativeExpressAd(0);
            }
        });
    }

    /**
     * Loads the Native Express ads in the items list.
     */
    private void loadNativeExpressAd(final int index) {

        if (index >= mRecyclerViewItems.size()) {
            return;
        }

        Object item = mRecyclerViewItems.get(index);
        if (!(item instanceof NativeExpressAdView)) {
            throw new ClassCastException("Expected item at index " + index + " to be a Native"
                    + " Express ad.");
        }

        final NativeExpressAdView adView = (NativeExpressAdView) item;

        // Set an AdListener on the NativeExpressAdView to wait for the previous Native Express ad
        // to finish loading before loading the next ad in the items list.
        adView.setAdListener(new AdListener() {
            @Override
            public void onAdLoaded() {
                super.onAdLoaded();
                // The previous Native Express ad loaded successfully, call this method again to
                // load the next ad in the items list.
                loadNativeExpressAd(index + ITEMS_PER_AD);
            }

            @Override
            public void onAdFailedToLoad(int errorCode) {
                // The previous Native Express ad failed to load. Call this method again to load
                // the next ad in the items list.
                Log.e("MainActivity", "The previous Native Express ad failed to load. Attempting to"
                        + " load the next Native Express ad in the items list.");
                loadNativeExpressAd(index + ITEMS_PER_AD);
            }
        });

        // Load the Native Express ad.
        adView.loadAd(new AdRequest.Builder().build());
    }


    private void loadJSON() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.myjson.com/") //https://api.myjson.com/bins/v4dzd
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RequestInterface request = retrofit.create(RequestInterface.class);
        Call<JSONResponse> call = request.getJSON();
        call.enqueue(new Callback<JSONResponse>() {
            @Override
            public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
                JSONResponse jsonResponse = response.body();

                mRecyclerViewItems = new ArrayList<Object>(Arrays.asList(jsonResponse.getKonten()));
                addNativeExpressAds();

                RecyclerView.Adapter adapter = new KontenAdapter(mRecyclerViewItems, getApplicationContext());
                mRecyclerView.setAdapter(adapter);
            }

            @Override
            public void onFailure(Call<JSONResponse> call, Throwable t) {
                Log.d("Error", t.getMessage());
            }
        });

    }

    /**
     * Adds {@link KontenItem}'s from a JSON file.
     */
    private void addMenuItemsFromJson() {
        try {
            String jsonDataString = readJsonDataFromFile();
            JSONArray menuItemsJsonArray = new JSONArray(jsonDataString);

            for (int i = 0; i < menuItemsJsonArray.length(); ++i) {

                JSONObject menuItemObject = menuItemsJsonArray.getJSONObject(i);

                String menuItemName = menuItemObject.getString("name");
                String menuItemDescription = menuItemObject.getString("description");
                String menuItemPrice = menuItemObject.getString("price");
                String menuItemCategory = menuItemObject.getString("category");
                String menuItemImageName = menuItemObject.getString("photo");
                String menuItemUrl = menuItemObject.getString("url");

                KontenItem kontenItem = new KontenItem(menuItemName, menuItemDescription, menuItemPrice,
                        menuItemCategory, menuItemImageName, menuItemUrl);
                mRecyclerViewItems.add(kontenItem);
            }
        } catch (IOException | JSONException exception) {
            Log.e(MainActivity.class.getName(), "Unable to parse JSON file.", exception);
        }
    }

    /**
     * Reads the JSON file and converts the JSON data to a {@link String}.
     *
     * @return A {@link String} representation of the JSON data.
     * @throws IOException if unable to read the JSON file.
     */
    private String readJsonDataFromFile() throws IOException {

        InputStream inputStream = null;
        StringBuilder builder = new StringBuilder();

        try {
            String jsonDataString = null;
            inputStream = getResources().openRawResource(R.raw.menu_items_json);
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(inputStream, "UTF-8"));
            while ((jsonDataString = bufferedReader.readLine()) != null) {
                builder.append(jsonDataString);
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }

        return new String(builder);
    }



}

这是MyAdapter

    public class KontenAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    // A menu item view type.
    private static final int MENU_ITEM_VIEW_TYPE = 0;

    // The Native Express ad view type.
    private static final int NATIVE_EXPRESS_AD_VIEW_TYPE = 1;

    // An Activity's Context.
    private final Context mContext;

    // The list of Native Express ads and menu items.
    private final List<Object> mRecyclerViewItems;


    public KontenAdapter(List<Object> recyclerViewItems, Context context) {
        this.mContext = context;
        this.mRecyclerViewItems = recyclerViewItems;
    }

    /**
     * The {@link MenuItemViewHolder} class.
     * Provides a reference to each view in the menu item view.
     */
    public class MenuItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
        private TextView menuItemName;
        private TextView menuItemDescription;
        private TextView menuItemPrice;
        private TextView menuItemCategory;
        private ImageView menuItemImage;
        private TextView menuItemUrl;

        MenuItemViewHolder(View view) {
            super(view);
            menuItemImage = (ImageView) view.findViewById(R.id.menu_item_image);
            menuItemName = (TextView) view.findViewById(R.id.menu_item_name);
            menuItemPrice = (TextView) view.findViewById(R.id.menu_item_price);
            menuItemCategory = (TextView) view.findViewById(R.id.menu_item_category);
            menuItemDescription = (TextView) view.findViewById(R.id.menu_item_description);
            menuItemUrl = (TextView) view.findViewById(R.id.menu_item_url);

            view.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {

            Intent detailIntent = new Intent(v.getContext(), PostActivity.class);
            detailIntent.putExtra("name",menuItemName.getText().toString() );
            detailIntent.putExtra("url", menuItemUrl.getText().toString());

            mContext.startActivity(detailIntent);

        }
    }

    /**
     * The {@link NativeExpressAdViewHolder} class.
     */
    public class NativeExpressAdViewHolder extends RecyclerView.ViewHolder {

        NativeExpressAdViewHolder(View view) {
            super(view);
        }
    }

    @Override
    public int getItemCount() {
        return mRecyclerViewItems.size();
    }

    /**
     * Determines the view type for the given position.
     */
    @Override
    public int getItemViewType(int position) {
        return (position % MainActivity.ITEMS_PER_AD == 0) ? NATIVE_EXPRESS_AD_VIEW_TYPE
                : MENU_ITEM_VIEW_TYPE;
    }

    /**
     * Creates a new view for a menu item view or a Native Express ad view
     * based on the viewType. This method is invoked by the layout manager.
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        switch (viewType) {
            case MENU_ITEM_VIEW_TYPE:
                View menuItemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(
                        R.layout.menu_item_container, viewGroup, false);
                return new MenuItemViewHolder(menuItemLayoutView);
            case NATIVE_EXPRESS_AD_VIEW_TYPE:
                // fall through
            default:
                View nativeExpressLayoutView = LayoutInflater.from(
                        viewGroup.getContext()).inflate(R.layout.native_express_ad_container,
                        viewGroup, false);
                return new NativeExpressAdViewHolder(nativeExpressLayoutView);
        }

    }

    /**
     *  Replaces the content in the views that make up the menu item view and the
     *  Native Express ad view. This method is invoked by the layout manager.
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int viewType = getItemViewType(position);
        switch (viewType) {
            case MENU_ITEM_VIEW_TYPE:
                MenuItemViewHolder menuItemHolder = (MenuItemViewHolder) holder;
                KontenItem kontenItem = (KontenItem) mRecyclerViewItems.get(position);

                // Get the menu item image resource ID.
                String imageName = kontenItem.getImageName();
                int imageResID = mContext.getResources().getIdentifier(imageName, "drawable",
                        mContext.getPackageName());

                // Add the menu item details to the menu item view.
                menuItemHolder.menuItemImage.setImageResource(imageResID);
                menuItemHolder.menuItemName.setText(kontenItem.getName());
                menuItemHolder.menuItemPrice.setText(kontenItem.getPrice());
                menuItemHolder.menuItemCategory.setText(kontenItem.getCategory());
                menuItemHolder.menuItemDescription.setText(kontenItem.getDescription());
                menuItemHolder.menuItemUrl.setText(kontenItem.getInstructionUrl());
                break;
            case NATIVE_EXPRESS_AD_VIEW_TYPE:
                // fall through
            default:
                NativeExpressAdViewHolder nativeExpressHolder =
                        (NativeExpressAdViewHolder) holder;
                NativeExpressAdView adView =
                        (NativeExpressAdView) mRecyclerViewItems.get(position);
                ViewGroup adCardView = (ViewGroup) nativeExpressHolder.itemView;
                // The NativeExpressAdViewHolder recycled by the RecyclerView may be a different
                // instance than the one used previously for this position. Clear the
                // NativeExpressAdViewHolder of any subviews in case it has a different
                // AdView associated with it, and make sure the AdView for this position doesn't
                // already have a parent of a different recycled NativeExpressAdViewHolder.
                if (adCardView.getChildCount() > 0) {
                    adCardView.removeAllViews();
                }
                if (adView.getParent() != null) {
                    ((ViewGroup) adView.getParent()).removeView(adView);
                }

                // Add the Native Express ad to the native express ad view.
                adCardView.addView(adView);
        }
    }

}

KontenItem.java

    class KontenItem {

    private final String name;
    private final String description;
    private final String price;
    private final String category;
    private final String imageName;
    private final String instructionUrl;

    public KontenItem(String name, String description, String price, String category,
                      String imageName, String instructionUrl) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.category = category;
        this.imageName = imageName;
        this.instructionUrl = instructionUrl;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public String getPrice() {
        return price;
    }

    public String getCategory() {
        return category;
    }

    public String getImageName() {
        return imageName;
    }

    public String getInstructionUrl() {
        return instructionUrl;
    }
}

提前致谢!

2 个答案:

答案 0 :(得分:0)

您正在呼叫addNativeExpressAds() for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) { final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);

在不同的主题中。因此,mRecyclerViewItems.get(i)在初始化之前使用。

我建议使用

 @Override
            public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
                JSONResponse jsonResponse = response.body();

                mRecyclerViewItems = new ArrayList<Object>(Arrays.asList(jsonResponse.getKonten()));
                addNativeExpressAds();

                RecyclerView.Adapter adapter = new KontenAdapter(mRecyclerViewItems, getApplicationContext());
                mRecyclerView.setAdapter(adapter);

setUpAndLoadNativeExpressAds(); // << ADD THIS METHOD HERE AND REMOVE THE OTHER CALLING
            }

答案 1 :(得分:0)

您的问题是mRecyclerViewItems是基于零的数组/集合。然而在setUpAndLoadNativeExpressAds()中,即使你没有元素,你也会迭代,然后要求元素0。

for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
  final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);
  ..
}

你应该

for (int i = 0; i < mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
  ..
}