ユーザーがリアルタイムで多くの画像を選択できるようにするカスタム ギャラリーを実装し、渡された画像ごとに 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
アイデアや提案はありますか?