When setting an ArrayList of Uris in the gridView, only one item is displayed. Why ?
Adapter:
public class ImageGridAdapter extends BaseAdapter {
private Context mContext;
public ImageGridAdapter(Context c) {
this.mContext = c;
}
#Override
public int getCount() {
try {
return PictureGroupActivity.ALofSelectedImgs.size();
} catch (NullPointerException e) {
return 0;
}
}
#Override
public Object getItem(int position) {
return PictureGroupActivity.ALofSelectedImgs.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (ImageView) convertView;
}
try {
imageView.setImageURI(PictureGroupActivity.ALofSelectedImgs.get(position));
Toast.makeText(mContext.getApplicationContext(), "Idee: " + PictureGroupActivity.ALofSelectedImgs, Toast.LENGTH_SHORT).show();
}catch (NullPointerException e) {}
return imageView;
}
Setting the adapter:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.picture_group_activity_layout);
GridView gridView = (GridView) findViewById(R.id.picture_group_gridView);
gridView.setAdapter(new ImageGridAdapter(this));
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(PictureGroupActivity.this, "You clicked " + position, Toast.LENGTH_SHORT).show();
}
});
}
From where I take the Image (After choosing from the phones gallery):
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK && null != data) {
selectedImage = data.getData();
ALofSelectedImgs = new ArrayList<Uri>();
ALofSelectedImgs.add(selectedImage);
Intent restart = getIntent();
finish();
startActivity(restart);
}
}
How do I make it add multiple Images into the ArrayList and make them stay there and not overwrite each other ?
There are a few problems that I can see with your approach here.
The simplest solution (changing the least code). Would be to move ALofSelectedImgs = new ArrayList(); from onActivityResult(int, int, Intent) and put it in onCreate(Bundle).
This still won't persist the data between orientation changes or closing the app. You'll get a new empty ArrayList every time onCreate(Bundle) is called.
And I wouldn't recommend using static fields like that. For starters, you can't use ImageGridAdapter with any other Activity or Fragment. You need to pass the List to it, either in the constructor or a setter method. That way you can reuse it more easily.
private Context mContext;
private List<Uri> mUris;
public ImageGridAdapter(Context context, List<Uri> uris) {
super(context);
mContext = context;
mUris = uris;
}
For a better solution... there are many ways to do this. Here's what I'd do:
Create a class extending SQLiteOpenHelper in order to save the selected Uris in an SQLite table. I learned a lot from this tutorial. I probably would not worry with a ContentProvider for something this simple. If you're interested I could pm you a template I use for keeping many tables' columns and other constants in a contract class.
Create an adapter extending SimpleCursorAdapter to create Views from a Cursor containing a query from your SQLite table. This is explained in the above tutorial.
In PhotoGroupActivity, perform a query of your table and initialize the adapter with the Cursor returned in onCreate(Bundle).
In onActivityResult(int, int, Intent) you need to insert the Uri into the SQLite table, then perform another query and give the adapter the new Cursor.
EDIT:
To answer your second question. This didn't occur to me at first, but you'll want to use thumbnails to display in your GridView. Use Bitmap.createScaledBitmap(Bitmap, int, int, boolean) to create the thumbnail. Store the thumbnail in your app's private storage to avoid it going in your gallery, then add the Uri to the thumbnail to your ArrayList. You'll probably want to keep track of the Uri for the full-size image as well for when the user touches the thumbnail.
Try using HashMap with the thumbnail Uri as the key and the full-size Uri as the value.
// Create the HashMap like this:
HashMap<Uri, Uri> uriMap = new HashMap<>();
// You have the main Uri. Get the bitmap, create a thumbnail and store it.
// Add an entry to the HashMap like this:
uriMap.put(thumbnailUri, fullSizeUri);
// To get the list of thumbnail Uris for the adapter:
List<Uri> thumbnailList = new ArrayList<>(uriMap.keySet());
// When user presses an image in GridView, get the relevant full-size
// Uri like this:
fullSizeUri = uriMap.get(thumbnailUri);
EDIT AGAIN:
I looked again and realised I suggested a CursorAdapter and then gave info on how to get a HashMap of the data.
Instead, just add another column to the SQLite table and store both Uris there. When you obtain the Uri and thumbnail Uri, store them both in the table and query the table for the SimpleCursorAdapter.
Related
I have built a gallery which has options to select multiple items and delete. I am loading images to GridView using custom BaseAdapter and while deleting I am using AsyncTask. But if try to delete multiple items getting array out of bound exception.
java.lang.IndexOutOfBoundsException: Invalid index x, size is x
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at com.android.Example.Adapters.ImageAdapter.getView(ImageAdapter.java:94)
I am getting this error only when if I use AsynTask, Deleting works fine if I do it in main thread.
I have no clue at which point my ArrayList is going out of bound.
This my Custom BaseAdapter
public class ImageAdapter extends BaseAdapter {
private com.nostra13.universalimageloader.core.ImageLoader imageLoader;
private Context mContext;
private int displayWidth;
private int imageWidth;
private ArrayList<String> f = new ArrayList<String>();// list of file paths
public ImageAdapter(Context c, ArrayList<String> f) {
mContext = c;
this.f=f;
imageLoader = ImageLoader.getInstance();
imageLoader.init(ImageLoaderConfiguration.createDefault(mContext));
DisplayMetrics metrics = new DisplayMetrics();
((Activity) c).getWindowManager().getDefaultDisplay().getMetrics(metrics);
displayWidth = metrics.widthPixels;
imageWidth=(displayWidth/3);
}
#Override
public int getCount() {
return this.f.size();
}
#Override
public Object getItem(int position) {
return this.f.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(imageWidth,imageWidth));
imageView.setPadding(Utils.dpToPx(2), Utils.dpToPx(2), Utils.dpToPx(2), Utils.dpToPx(2));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setCropToPadding(true);
imageView.setBackground(mContext.getResources().getDrawable(R.drawable.gridview_selector));
} else {
imageView = (ImageView) convertView;
}
//imageView.setAdjustViewBounds(true);
imageLoader.displayImage("file:///"+this.f.get(position),imageView,); //This is line number 94
return imageView;
}
}
And this how I am deleting.
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
ArrayList<Uri> imageUris = new ArrayList<Uri>();
SparseBooleanArray checked = gridView.getCheckedItemPositions();
int checkedItemCount = gridView.getCheckedItemCount();
switch (item.getItemId()) {
case R.id.delete:
new DeleteAsync(checked, checkedItemCount).execute();
return true;
And this my AsyncTask
private class DeleteAsync extends AsyncTask<Void, Void, Void> {
SparseBooleanArray _checked;
int _checkedItemCount;
private DeleteAsync(SparseBooleanArray _checked, int _checkedItemCount) {
this._checked = _checked;
this._checkedItemCount = _checkedItemCount;
}
#Override
protected Void doInBackground(Void... params) {
for (int i = (_checkedItemCount - 1); i >= 0; i--) {
if (_checked.valueAt(i)) {
File file = new File(files.get(_checked.keyAt(i)));
if (file.delete()) {
MediaScannerConnection.scanFile(getActivity(), new String[]{file.toString()}, null, null);
files.remove(_checked.keyAt(i));
} else
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(rootView.getContext(), getResources().getString(R.string.delete_error), Toast.LENGTH_SHORT).show();
}
});
}
}
}
#Override
protected void onPostExecute(Void void) {
super.onPostExecute(void);
imageAdapter.notifyDataSetChanged();
progress.setVisibility(View.INVISIBLE);
}
}
Setting adapter to GridView
ArrayList<String> files=getListOfImagFiles();
imageAdapter= new ImageAdapter(rootView.getContext(), files);
gridView.setAdapter(imageAdapter);
Check the size of array before deleting.
Something like this may work.
if(items.size())>0
for (int i = _checkedItemCount; i > 0; i--)
{
//do your stuff
}
This is a common problem in getView of gridview adapters. For safe side I have followed like this.
put a condition check in getView()
if(position<this.f.size())
{
imageLoader.displayImage("file:///"+this.f.get(position),imageView,); //This is line number 94
}
else
{
//Display some default img or error img.
}
Suggestion:
Do not delete items of array based on the position of item. Delete based on the item model object.
Follow MVC guidelines.
Currently it is giving issue as
When you start you have total 10 items in files list. So index will be 0-9 and your for loop runs for this index range.
Now when you delete entry from files arraylist your count decreases by one i.e. it becomes Now you index range will be 0-8.
So as you go on deleting items from files arraylist your this line of code
imageLoader.displayImage("file:///"+this.f.get(position),imageView,);
inside baseadapter will loose that position.
So you are left with two options:
Do not delete from files arraylist just keep track of filenames and in onpostexecute delete those items from files arraylist and use notifydatasetchanged on your baseadapter.
Delete item from files arraylist but on each deletion use onprogressupdate to call notifydatasetchanged on your baseadapter.
Weirdly I solved my problem just putting safe guard before loading image.
if(position<getCount())
imageLoader.displayImage("file:///"+this.f.get(position),imageView,options);
I am not accepting my answer as I think this is not the perfect solution for this problem and also I have no idea why this is happening only when I am using AsyncTask
Do setAdapter again instead of NotifyDataSetChanged, it will reload the list and position will be correct.
I have a listview in the layout, and each item has two part: one is a user avatar (ImageView), and other is a chat content ( TextView). Looks like:
And I have a custom adapter.
I would like to implement : when I click the avatar, I can go to the system gallery and select a photo as the avatar.
So my code about onClickListener in adapter class is:
Intent itent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
((Activity)(context)).startActivityForResult(itent, LOAD_IMAGE_RESULT);
And I also override the onActivityResult method in activity.
However, the solution I mentioned above cannot change the avatar in onActivityResult method, because I don't know how to communicate between adapter and activity.
Hope for a solution.
EDIT - Initially, I have just shown one of the method to update child views only by leaving it's adapter in the inconsistent state (because the original question don't give much information about underlying data structure).
Adapter: An Adapter object acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to
the data items. The Adapter is also responsible for making a View for
each item in the data set.
Note: As the definition itself describes that it's important to maintain the state means (data + view). So you should always have the consistent user experience.
To give the example properly, I have also defined the data model class ItemData based on the assumption of given image in original question.
/**
* Model Class
*/
public class ItemData {
private Uri imageUri;
private String msg;
private Date timeStamp;
public void setImageUri(Uri uri) {
this.imageUri = uri;
}
public Uri getImageUri() {
return this.imageUri;
}
...
}
CustomAdapter
Create the custom adapter for listView which will maintain the child views with it's dataset accordingly. You've to maintain the reference of last selected row index lastSelectedIndexRow which can be used later for updating the view.
Note: To get the view for any index in the listView, we should not call getView() method of the adapter. As calling getView() with null for the convertView causes the adapter to inflate a new view from the adapter's layout resource (does not get the view that is already being displayed).
The AdapterView should always be updated with notifyDataSetChanged() based on the current dataset hold by adapter.
public class CustomAdapter extends BaseAdapter {
private Context context;
private LayoutInflater inflater;
private List<ItemData> dataList;
private int lastSelectedRowIndex = -1;
public static int LOAD_IMAGE_RESULT = 201;
public CustomAdapter(Context context, ArrayList<ItemData> dataList) {
// hold the items
this.context = context;
this.dataList = dataList;
this.inflater = LayoutInflater.from(this.context);
}
#Override
public int getCount() {
return (dataList != null && !dataList.isEmpty()) ? dataList.size() : 0;
}
#Override
public ItemData getItem(int position) {
return (dataList != null && !dataList.isEmpty()) ? dataList.get(position) : null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderItem viewHolder;
if (converView == null) {
// inflate the layout
convertView = inflater.inflate(R.layout.row_item, parent, false);
// well set up the ViewHolder
viewHolder = new ViewHolderItem();
viewHolder.avatar = (ImageButton)view.findById(R.id.avatar);
// store the holder with the view.
convertView.setTag(viewHolder);
}
else {
// we've just avoided calling findViewById() on resource every time
// just use the viewHolder
viewHolder = (ViewHolderItem) convertView.getTag();
}
// Set row data
ItemData data = (ItemData)getItem(position);
if (data != null) {
// set message
viewHolder.message.setText(data.getMessage());
// set formatted timestamp
String formattedTimeStamp = ...; // convert data.getTimeStamp() into formatted version
viewHolder.timeStamp.setTextView(formattedTimeStamp);
// set Image and also it's action.
viewHolder.avatar.setImageUri(data.getImageUri());
viewHolder.avatar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
Intent intent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
((Activity)context).startActivityForResult(intent, LOAD_IMAGE_RESULT);
} catch (Exception e) {
Log.e("Demo application", "Failed to invoke call", e);
}
}
}
}
}
/**
* Get the last selected item index
*
*/
public int getLastSelectedItemIndex() {
return lastSelectedRowIndex;
}
/**
* Update the adapater with new data
*
*/
public void updateItems(List<ItemData> dataList) {
if (this.dataList != dataList)
this.dataList = dataList;
// update the view.
notifyDataSetChanged();
// reset the last selection
lastSelectedRowIndex = -1;
}
/**
* Hold View items
*/
static class ViewHolderItem {
private ImageView avatar;
private TextView message;
private TextView timeStamp;
}
}
ExampleActivity
In activity, you've to defined the reference of custom adapter and handle the image selection results in onActivityResult() method. Once you get the selected image from gallery then update the underlying data holding inside dataList and also update the adapter by calling updateItems() custom method.
The updateItems() method of adapter will take the new data list as argument and invalidate the adapterView by calling notifyDataSetChanged().
public class ExampleActivity extend FragmentActivity {
private ListView listView;
private CustomAdapter adapter;
private List<ItemData> dataList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dataList = ...; // Load the list from database
// Create the custom adapter with filled list items.
adapter = new CustomAdapter(this, dataList);
// List View and set the data adapter
listView = (ListView)findViewById(R.id.listView);
listView.setAdapter(adapter);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Check if it's coming from MediaStore Selection.
if (resultCode == Activity.RESULT_OK &&
requestCode == CustomAdapter.LOAD_IMAGE_RESULT) {
// Get the selected rowIndex
if (adapter != null && dataList != null) {
// check the row index is valid
int rowIndex = adapter.getLastSelectedItemIndex();
if (rowIndex > -1 && rowIndex < dataList.size()) {
// Get the item
ItemData item = dataList.get(rowIndex);
// Update the item with imageUri
item.setImageUri(Uri.parse(data.getData()));
/**
* If you may want to update the information in database,
* then it's the best place, but please do in background thread.
*/
//Now notify the adapter with new changes
adapter.updateItems(dataList);
}
}
}
}
...
}
You should save a reference to the clicked view in the onClickListener method and have a public method in your adapter. When you get the response in the onActivityResult method call the adapter's method and use the
Add a public method in your custom adapter to set the image (like public void setAvatar(Bitmap image))
Then, in your onActivityResult method, get the image selected and use your newly created method to set the new image in your adapter. (see here for more informations: Getting a Result from an Activity
Don't forget to put notifyDataSetChanged(); at the end of your your newly method in your adapter to refresh all the list.
public void setAvatar(Bitmap image)
{
...
imageView.setImageBitmap(image);
...
notifyDataSetChanged();
}
In addition, if you need to know which image was clicked, you can specify an Integer as a parameter and check it in the onActivityResult.
I see you use a constant LOAD_IMAGE_RESULT for this parameter. Instead of a constant, put an integer who identify the clicked item.
In the adapter, (not sure which kind of adapter you using), you set the onClickListener for the ImageView. Hence,
mImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
<INSERT CODE HERE>
mContext.startActivity(intent);
}
});
Where mImageView is the ImageView for that item. Also a good idea might be to use an ImageButton since it allows Images and is a button which is what you want. Then by the add the code you want to select the Image. You may need to pass context through to the Adapter too (but you would generally do that anyway). Then you can process the onActivityResult method in the activity that the adapter is in i.e. setting the Image to the image you picked. There are a couple ways to do this.
I just started into this Android development thing, by my own, basically reading various books and searching the internet and the forums for the information that i need, but it seems I got to a halt. I am trying to make an application by which the user will be prompted with a list of images in a gridview. Upon clicking an image, the application will sent the user to an activity which will display a, sometimes short, sometimes long, story that is read from a .txt file placed inside the assets folder. I can do it the easy way, ie. making an activity for each .txt that needs to be opened but i'm talking 50+ image files in gridview and 50+ .txt files in assets folder. So I want to do this from only 2 activities, main and +1, or as few as possible. below is the code i got so far.
MainActivity.java
public class MainActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gridview = (GridView) findViewById (R.id.gridv);
gridview.setAdapter(new ImageAdapter (this));
gridview.setOnItemClickListener (new AdapterView.OnItemClickListener() {
public void onItemClick (AdapterView<?> parent, View v, int position, long id) {
Intent i = new Intent (getApplicationContext(), ChronText.class);
i.putExtra("id", position);
startActivity (i);
}
});
}
}
ChronText.java
public class ChronText extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.flowtext);
Intent i = getIntent();
int position = i.getExtras().getInt("id");
TextView chronview = (TextView) findViewById (R.id.chronview);
AssetManager assetManager = getAssets();
InputStream input;
try {
input = assetManager.open("");
int size = input.available();
byte[] buffer = new byte[size];
input.read(buffer);
input.close();
String text = new String (buffer);
chronview.setText(text);
} catch (IOException e) {
e.printStackTrace();
}
}
}
ImageAdapter.java
public class ImageAdapter extends BaseAdapter {
private Context myContext;
public Integer[] myThumbsId = {
R.drawable.breathingspace,
R.drawable.particletracks,
R.drawable.welcomeparty,
R.drawable.thebookofemptiness2of2,
R.drawable.uplifted
};
public ImageAdapter (Context c) {
myContext = c;
}
public int getCount() {
return myThumbsId.length;
}
public Object getItem (int position) {
return myThumbsId [position];
}
public long getItemId (int position) {
return 0;
}
public View getView (int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView (myContext);
imageView.setLayoutParams(new GridView.LayoutParams(170,111));
imageView.setImageResource(myThumbsId[position]);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(5, 5, 5, 5);
return imageView;
}
}
The application works, however, I have to manually enter the filename in ChronText.java
InputStream input;
try {
input = assetManager.open("");
so basically, i would need to manually write the filename for each image that is clicked. Is there a way this thing could be done automatically? And if yes, how? The way I am thinking is to make a list containing all the filenames, like the gridview has, that contains the files in the corresponding order of the gridview images, and when the user clicks the image, the activity is created for the file that has the same position in filename list as the image in the gridview list. If there is another, simpler way, please, do tell/explain. If my idea is good, please tell me how to put it 'on paper'. I worked on this app for some weeks now and i'm a bit burned out. Thanks in advance.
After another week or so of head smashing, I have fount the answer and completed the app. I can show the result if anyone wishes to
I am currently attempting to make a simple image gallery like the now deprecated Android Gallery. From this gallery the user can also navigate to a simple image editor by selecting an image form the gallery. After a lot of Googling I managed to find a HorizontalListView here which is exactly what I need. Initially I had a lot of success with this by just inserting the images in my folder as compressed bitmaps. Since then however I have found this video from Google I/O in which they create an image gallery and an image editor; similar to what I am attempting to create. There are however two main differences between my app and theirs:
they use a GridView for their gallery and I use the aforementioned HorizontalListView
I am attempting to only load images from a specified target path rather than just all images on the SD card.
So far I am unable to adapt their code to mine as none of the images are loading into my gallery. As with the video I use an AsyncTask to load my thumbnails:
private class ThumbnailAsyncTask extends AsyncTask<Long, Void, Bitmap>
{
//The ImageView we will be adding the thumbnail to
private final ImageView mTarget;
public ThumbnailAsyncTask(ImageView target)
{
mTarget = target;
}
#Override
protected Bitmap doInBackground(Long... params)
{
final long photoID = params[0];
final Bitmap result = MediaStore.Images.Thumbnails.getThumbnail(
getContentResolver(), photoID, MediaStore.Images.Thumbnails.MINI_KIND, null);
return result;
}
#Override
protected void onPreExecute()
{
mTarget.setTag(this);
}
#Override
protected void onPostExecute(Bitmap result)
{
if (mTarget.getTag() == this)
{
mTarget.setImageBitmap(result);
mTarget.setTag(null);
}
}
}
and I am using a CursorAdapter for the images in the gallery:
private class PhotoAdapter extends CursorAdapter
{
public PhotoAdapter(Context context)
{
super(context, null, false);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
return LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor)
{
final long photoId = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
/*
* Cancel any pending thumbnail task, since this view is now bound
* to new thumbnail
*/
final ThumbnailAsyncTask oldTask = (ThumbnailAsyncTask) imageView.getTag();
if (oldTask != null)
oldTask.cancel(false);
/*
* If we arrived here, either cache is disabled or cache miss, so we
* need to kick task to load manually
*/
final ThumbnailAsyncTask task = new ThumbnailAsyncTask(imageView);
imageView.setImageBitmap(null);
imageView.setTag(task);
task.execute(photoId);
}
}
With the following CursorLoader
final LoaderCallbacks<Cursor> mCursorCallbacks = new LoaderCallbacks<Cursor>()
{
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
final String[] columns = {BaseColumns._ID};
return new CursorLoader(NewProjectActivity.this,
Uri.fromFile(new File(mTargetPath)), columns, null, null,
MediaStore.Images.Media.DATE_ADDED + " DESC");
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader)
{
mAdapter.swapCursor(null);
}
};
getLoaderManager().initLoader(LOADER_CURSOR, null, mCursorCallbacks);
Any ideas on why none of my images are loading?
The basic problem here is, bindView() method is not waiting for asynctask's result. You need to notify your list view when some of its content get changed.
You can do the following changes.
1) Change task.execute(photoId); to task.execute(photoId).get(); this will force your bindView() method to wait till you get your image. This approach is not advisable but it will help you to understand the exact problem.
OR
2) In onPost() of asynctask, invalidate your list view's old content and try to reload it with new content.
mAdapter.notifyDataSetChanged();
mAdapter.notifyDataSetInvalidated();
OR
3) Personally i will suggest you to finish all network operation first (i.e. fetching images ) by changing your code structure and then after try to set your adapter with the prefetched data. This is not a perfect solution but it worked in my case.
I have an app with the GridView layout have a single image for testing purposes until I figure out this next step. I have everything set up, but I don't know how implement a design where image A, B, C, D, etc. are clicked result in the user (me) landing on a webpage that I specify. I need each image to link to different locations and I would really appreciate the help to implement this into my code:
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create new ImageView for each item
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(150, 150));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(5, 5, 5, 5);
}
else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
// references to images
private Integer[] mThumbIds = {
R.drawable.A, R.drawable.B,
R.drawable.C, R.drawable.D,
R.drawable.E, R.drawable.F,
R.drawable.G, R.drawable.H,
};
}
Use the getItemId() in your adapter (just return the position) to select the URL you want to go to. Just like you have mThumbIds, just have a parallel array that holds the matching URLs, so when an image is clicked, its corresponding URL can be accessed easily at the same index.
The "zero-work" method of doing this would be to simply launch an Intent that would open the URL in the device's browser from an OnItemClickListener. Something like:
//Obtain a reference to the view in your layout somehow...I chose findViewById()
GridView grid = (GridView)findViewById(R.id.peters_grid);
grid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
//"position" is the location of the image clicked
String url = arrayOfUrls[position];
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
}
});
Where you are grabbing the correct URL for each position from an array of Strings. This is just one idea for getting the right URL...you could construct the proper URL a million different ways, but the key is to pass it to the Intent and then fire it.
The "little work" version of this would be to create a WebView in this or another Activity so you can load the web page and stay within your application code. If the WebView is in a second Activity, it would still be best to use an Intent to pass the URL string from the first Activity to the second.
Hope that Helps!