0

ユーザーがリアルタイムで多くの画像を選択できるようにするカスタム ギャラリーを実装し、渡された画像ごとに ImageView が実用的に作成される新しいアクティビティにそれらを渡します。ユーザーは自由に何度でも画像を追加できます。私が気づいたのは、ユーザーがまだ選択されていない新しい画像を毎回選択すると、すべてが正しく機能することです。しかし、ユーザーが次のアクティビティに既に渡されている画像を選択すると、アプリがクラッシュし、java.lang.StringIndexOutOfBounds エラーが発生します。この問題の原因を理解する必要があります。コードを追加しますが、理解を深める必要がある場合は、編集または追加できます。

カスタム ギャラリー:

public void onResume(){
    super.onResume();
    Intent previousIntent= getIntent(); //create a new intent for retrieve the photos path from the PhotoManagement activity (this need to be done for not lost the already selected images)
    Bundle customGalleryIntentBundle = getIntent().getExtras();
    if( customGalleryIntentBundle != null ){
        photosPath = (String) customGalleryIntentBundle.get("Selected Images"); //get the selected images from the previous activity
        photosPath = photosPath.replace("null", "");
        Log.i("CUSTOM GALLERY PHOTOS PATH",""+photosPath);
    }

    final String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID };
    final String orderBy = MediaStore.Images.Media._ID;
    Cursor imagecursor = managedQuery( //create the cursor for navigate inside the database device and retrieve informations about the media contents like photo, video ecc...
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null,
            null, orderBy);
    int image_column_index = imagecursor.getColumnIndex(MediaStore.Images.Media._ID); //get the current column index
    this.count = imagecursor.getCount(); //count the external content uri of the cursor
    this.thumbnails = new Bitmap[this.count]; //create an array of bitmap using the size retrieved from the count variable
    this.arrPath = new String[this.count]; //create the array that contains the list of the path of the media (photos in this case)
    this.thumbnailsselection = new boolean[this.count]; //initialize the array of boolean to save informations about the selection of the thumbnails
    for (int i = 0; i < this.count; i++) {
        imagecursor.moveToPosition(i); //let's start from the first position and move one by one until the end
        int id = imagecursor.getInt(image_column_index);
        int dataColumnIndex = imagecursor.getColumnIndex(MediaStore.Images.Media.DATA);
        thumbnails[i] = MediaStore.Images.Thumbnails.getThumbnail( //take the thumbnails of the images and store it inside the array using the MICRO format for avoid high memory usage
                getApplicationContext().getContentResolver(), id,
                MediaStore.Images.Thumbnails.MICRO_KIND, null);
        arrPath[i]= imagecursor.getString(dataColumnIndex);
    }
    GridView imagegrid = (GridView) findViewById(R.id.PhoneImageGrid); //assign the GridView to the equivalent element in the xml file
    imageAdapter = new ImageAdapter(); //create a new imageAdapter. It is used how source for all the items in the gridView
    imagegrid.setAdapter(imageAdapter);
    imagecursor.close(); //close the image cursor when we arrive to the end of the columns to analize

    final Button selectBtn = (Button) findViewById(R.id.selectBtn); //assign the button to the equivalent in the xml layout
    selectBtn.setOnClickListener(new OnClickListener() { //create a listener for reveal when it is pressed

        public void onClick(View v) {
            // TODO Auto-generated method stub
            final int len = thumbnailsselection.length;
            int cnt = 0; //initialize a count for check the number of selected photos
            String selectImages = "";
            for (int i =0; i<len; i++)
            {
                if (thumbnailsselection[i]){
                    cnt++;
                    selectImages = selectImages + arrPath[i] + "|"; //store the correct path of the images in the variable (we can use this string for find the images path in others activity)         
                    Log.i("CUSTOM GALLERY SELECT IMAGES",""+selectImages);
                }
            }
            //if no one image is selected show a message to the user
            if (cnt == 0){
                Toast.makeText(getApplicationContext(),
                        "Please select at least one image",
                        Toast.LENGTH_LONG).show();
            } else {
                //If at least one image is selected we create a new activity where the images are displayed and the user can manage them
                Intent photoManagementIntent = new Intent(
                        getApplicationContext(),
                        PhotoManagement.class //select the class for the next activity
                        );
                photoManagementIntent.putExtra("Selected Images",""+selectImages+photosPath); //add an extra parameter that contains the path of the selected images. In this mode we can read it in the next activity
                startActivity(photoManagementIntent); //start the new activity
            }
        }
    });
}

これは、カスタム ギャラリーから渡された画像を表示するために、実用的に ImageViews を作成するクラスです。

protected void onResume(){
    super.onResume();
        Intent previousIntent= getIntent(); //create a new intent for retrieve informations from the previous activity
        Bundle photoManagementIntentBundle = getIntent().getExtras();
        if(photoManagementIntentBundle != null) //if the new intent is different from null
        {
            photosPath = (String) photoManagementIntentBundle.get("Selected Images"); //get the "selected images" string created in the previous activity
            photosPath = photosPath.replace("null", "");
            final String photosPathCopy = photosPath;
            int count = photosPath.length() - photosPath.replace("|", "").length(); //count contains the number of images selected

            //SCROLL VIEW
            ScrollView scrollView = new ScrollView(this); //create a new scrollView
            scrollView.setBackground(getResources().getDrawable(R.drawable.background)); //give the background gradient
            scrollView.setLayoutParams(new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, //set the main params about the dynamic size of the scrollView
                                                         ScrollView.LayoutParams.MATCH_PARENT));
            scrollView.setPadding(0, 20, 0, 0);

            //LINEAR LAYOUT
            LinearLayout linearLayout = new LinearLayout(this); //create a new linearLayout
            linearLayout.setOrientation(LinearLayout.VERTICAL); //set the layout orientation
            linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT));

            for(int i=0; i<count; i++) {
                int indexOf = photosPath.indexOf('|'); //take the number of characters from | char
                String correctPath = photosPath.substring(0, indexOf); //this take the correct path of the photo
                photosPath = photosPath.replace(correctPath+"|", ""); //replace the old path with nothing. In this mode at every loop we have a new path until the end.
                Log.i("PHOTO MANAGEMENT PHOTOS PATH INSIDE LOOP",""+photosPath);
                //RELATIVE LAYOUT
                RelativeLayout relativeLayout = new RelativeLayout(this); //create a new relative layout
                relativeLayout.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, //set main params about the width and height
                        RelativeLayout.LayoutParams.FILL_PARENT));
                relativeLayout.setBackgroundColor(getResources().getColor(R.color.grayColor)); //set background color
                LinearLayout.LayoutParams relativeParams = new LinearLayout.LayoutParams(
                        new LinearLayout.LayoutParams(
                                LinearLayout.LayoutParams.MATCH_PARENT,
                                LinearLayout.LayoutParams.WRAP_CONTENT));
                relativeParams.setMargins(20, 20, 20, 0);
                relativeLayout.setLayoutParams(relativeParams); //set declared params about layout to the relativeLayout
                relativeLayout.requestLayout();
                relativeLayout.setOnClickListener(new View.OnClickListener(){ //create a listener about the layout. When a user press a point inside the relative layout a new activity should be created
                     @Override
                     public void onClick(View v){
                         Intent photoDetailsActivity = new Intent(
                                getApplicationContext(),
                                PhotoDetails.class //assign the class for create a new intent
                            );
                        photoDetailsActivity.putExtra("Selected Images",""+photosPathCopy);
                        startActivity(photoDetailsActivity); //let's start the new activity
                     }
                 });

                //IMAGE VIEW
                ImageView selectedPhoto = new ImageView(this); //create a new imageView
                selectedPhoto.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT)); //set width and height params about the selected photo zone
                try {
                    File standardImage = new  File(correctPath); //create a new file with the original path of the images
                    BitmapFactory.Options o = new BitmapFactory.Options(); //Enable the Bitmap factory options
                    o.inJustDecodeBounds = true; //allow the caller to query the bitmap without having to allocate the memory for its pixels
                    BitmapFactory.decodeStream(new FileInputStream(standardImage),null,o); //decode the image using the bitmap factory options

                    final int REQUIRED_SIZE=70; //The new size we want to scale to (save big quantity of memory)
                    int scale=1; //we need to find the correct scale value. It should be the power of 2.
                    while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
                        scale*=2;
                    BitmapFactory.Options o2 = new BitmapFactory.Options();  
                    o2.inSampleSize=scale; //decode with inSampleSize
                    selectedPhoto.setImageBitmap(BitmapFactory.decodeStream(new FileInputStream(standardImage), null, o2)); //assign the scaled image to the gridView
                } catch (FileNotFoundException e) {
                    Log.i("Error","File not found");
                }
                selectedPhoto.getLayoutParams().height = 90; //use a fixed size for each thumnail
                selectedPhoto.getLayoutParams().width = 90;

                //TEXT VIEWS
                TextView numberCopies = new TextView(this); //create new TextView
                numberCopies.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
                numberCopies.setGravity(Gravity.CENTER); //set position to the center in confront to the parent
                numberCopies.setPadding(25, 25, 25, 25);
                numberCopies.setTextColor(getResources().getColor(R.color.blackColor));
                numberCopies.setText("2 copies "); //this need to be dynamic
                RelativeLayout.LayoutParams layoutParamsNumberCopies = (RelativeLayout.LayoutParams) numberCopies.getLayoutParams();
                layoutParamsNumberCopies.addRule(RelativeLayout.CENTER_HORIZONTAL); //add a rule to the layout params. We put his position at the horizontal center of the relative layout
                numberCopies.setLayoutParams(layoutParamsNumberCopies); //set the layout rules to the textView

                TextView priceCopies = new TextView(this);
                priceCopies.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
                priceCopies.setGravity(Gravity.CENTER);
                numberCopies.setPadding(25, 25, 25, 25);
                priceCopies.setTextColor(getResources().getColor(R.color.redColor));

                RelativeLayout.LayoutParams layoutParamsPriceCopies = (RelativeLayout.LayoutParams) priceCopies.getLayoutParams();
                layoutParamsPriceCopies.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                layoutParamsPriceCopies.addRule(RelativeLayout.CENTER_VERTICAL);
                priceCopies.setLayoutParams(layoutParamsPriceCopies);

                relativeLayout.addView(selectedPhoto);
                relativeLayout.addView(numberCopies);
                relativeLayout.addView(priceCopies);
                linearLayout.addView(relativeLayout);
            }               
            scrollView.addView(linearLayout);
            setContentView(scrollView);
            linearLayout.addView(relativeLayoutOpenButton); //add the button to the view
        }
}

次のアクティビティに既に追加されている画像を追加しようとすると、次のエラーが表示されます。

10-04 09:15:20.019: E/AndroidRuntime(1715): FATAL EXCEPTION: main
10-04 09:15:20.019: E/AndroidRuntime(1715): java.lang.RuntimeException: Unable to resume activity {com.example.dilandprints2/com.example.dilandprints2.PhotoManagement}: java.lang.StringIndexOutOfBoundsException: length=0; regionStart=0; regionLength=-1
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2742)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2771)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2235)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.access$600(ActivityThread.java:141)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.os.Looper.loop(Looper.java:137)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.main(ActivityThread.java:5041)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.reflect.Method.invokeNative(Native Method)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.reflect.Method.invoke(Method.java:511)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at dalvik.system.NativeStart.main(Native Method)
10-04 09:15:20.019: E/AndroidRuntime(1715): Caused by: java.lang.StringIndexOutOfBoundsException: length=0; regionStart=0; regionLength=-1
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.String.startEndAndLength(String.java:583)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.String.substring(String.java:1464)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at com.example.dilandprints2.PhotoManagement.onResume(PhotoManagement.java:93)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1185)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.Activity.performResume(Activity.java:5182)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2732)
10-04 09:15:20.019: E/AndroidRuntime(1715):     ... 12 more

アイデアや提案はありますか?

4

2 に答える 2