My application is able to take pictures and to save and to show them in a list. They also can be deleted and the list is correctly updated.
However, if I close the application and then launch it again, the list is no more correct. In detail:
These are the list elements in the adapter after I take two pictures:
List[0] 20150603_042556
List[1] 20150603_042601
These pictures are correctly saved. However, if I close and open again the application, this is what happens:
Loaded selfie:﹕ 20150603_042556
List[0] 20150603_042556
Loaded selfie: 20150603_042601
List[0] 20150603_042601
List[1] 20150603_042601
This is the add function:
public void add(SelfieRecord listItem) {
list.add(listItem);
for(int k=0;k<list.size();k++)
Log.i("List: ", String.valueOf(k) + " " + list.get(k).getPicName());
notifyDataSetChanged();
}
This is how I load the saved pictures:
for (int ii = 0; ii < dirFiles.length; ii++) {
File file = dirFiles[ii];
String path = file.getAbsolutePath();
selfie.setPic(path);
selfie.setPicName(path.substring(path.indexOf("_") + 1, path.lastIndexOf("_")));
Log.i(TAG+" Loaded selfie: ",path);
mAdapter.add(selfie);
}
Can't figure out what happens.
EDIT: More code follows.
In the main activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// setup notifications
setNotifications();
mListView = (ListView)findViewById(R.id.selfieList);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(MainActivity.this, ViewActivity.class);
SelfieRecord record = (SelfieRecord) mAdapter.getItem(position);
intent.putExtra(SELFIE_KEY, record.getPic());
startActivity(intent);
}
});
mAdapter = new SelfieAdapter(getApplicationContext());
mListView.setAdapter(mAdapter);
// load selfies
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && mAdapter.getCount()==0) {
// gets the files in the directory
File fileDirectory = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath());
if (!fileDirectory.exists()) {
fileDirectory.mkdirs();
}
// lists all the files into an array
File[] dirFiles = fileDirectory.listFiles();
if (dirFiles.length != 0) {
SelfieRecord selfie = new SelfieRecord();
// loops through the array of files, outputing the name to console
for (int ii = 0; ii < dirFiles.length; ii++) {
File file = dirFiles[ii];
String path = file.getAbsolutePath();
selfie.setPic(path);
selfie.setPicName(path.substring(path.indexOf("_") + 1, path.lastIndexOf("_")));
Log.i(TAG+" Loaded selfie: ",path);
mAdapter.add(selfie);
}
}
}
registerForContextMenu(mListView);
}
In the Adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View newView = convertView;
ViewHolder holder;
SelfieRecord curr = list.get(position);
Log.i("List: ", String.valueOf(position)+" "+list.get(position).getPicName());
if (null == convertView) {
holder = new ViewHolder();
newView = inflater.inflate(R.layout.list_record, parent, false);
holder.pic = (ImageView) newView.findViewById(R.id.pic);
holder.name = (TextView) newView.findViewById(R.id.pic_name);
newView.setTag(holder);
} else {
holder = (ViewHolder) newView.getTag();
}
//setPic(holder.pic, curr.getPic());
holder.name.setText(curr.getPicName());
mImageView = holder.pic;
new BitmapLoader().execute(curr.getPic());
Log.i("Loader: ", String.valueOf(position) + " " + curr.getPicName());
return newView;
}
static class ViewHolder {
ImageView pic;
TextView name;
}
public void add(SelfieRecord listItem) {
list.add(listItem);
for(int k=0;k<list.size();k++)
Log.i("List: ", String.valueOf(k) + " " + list.get(k).getPicName());
notifyDataSetChanged();
}
public void remove(int position) {
list.remove(position);
notifyDataSetChanged();
}
Bitmap Loader:
public class BitmapLoader extends AsyncTask<String,Integer,Bitmap> {
#Override
protected Bitmap doInBackground(String... resId) {
// Get the dimensions of the View
int targetW = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 160, mContext.getResources().getDisplayMetrics()));
int targetH = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120, mContext.getResources().getDisplayMetrics()));
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(resId[0], bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(resId[0], bmOptions);
return bitmap;
}
#Override
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
I believe the problem is when I put the elements in the adapter after recovered them from storage:
Loaded selfie:﹕ 20150603_042556
List[0] 20150603_042556
Loaded selfie: 20150603_042601
List[0] 20150603_042601
List[1] 20150603_042601
The second call to the adapter's add method overwrite the first element. Why?
Solved by changing the for loop in on Create:
if (dirFiles.length != 0) {
// loops through the array of files, outputing the name to console
for (int ii = 0; ii < dirFiles.length; ii++) {
File file = dirFiles[ii];
String path = file.getAbsolutePath();
SelfieRecord selfie = new SelfieRecord();
selfie.setPic(path);
selfie.setPicName(path.substring(path.indexOf("_") + 1, path.lastIndexOf("_")));
Log.i(TAG+" Loaded selfie: ",path);
mAdapter.add(selfie);
}
}
Must pass a different SelfieRecord Object to the add method.
Related
I have a Gridview in my App...
The Gridview shows all the available files in a particular folder, lets say 'Sample Folder'...
What I am trying is to pass the ACTION_VIEW Intent on item click but I am not able to set its Data and Type...
Most of the files are either image or videos...
Here's the code for Activity -
public class Downloaded extends AppCompatActivity {
// Declare variables
private String[] FilePathStrings;
private String[] FileNameStrings;
private File[] listFile;
GridView grid;
GridViewAdapter adapter;
File file;
TextView empty;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
// Check for SD Card
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
Toast.makeText(Downloaded.this, "Error! No SDCARD Found!", Toast.LENGTH_LONG)
.show();
} else {
// Locate the image folder in your SD Card
file = new File(Environment.getExternalStorageDirectory()
+ File.separator + "Sample");
file.mkdirs();
}
if (file.isDirectory()) {
listFile = file.listFiles();
// Create a String array for FilePathStrings
FilePathStrings = new String[listFile.length];
// Create a String array for FileNameStrings
FileNameStrings = new String[listFile.length];
for (int i = 0; i < listFile.length; i++) {
// Get the path of the image file
FilePathStrings[i] = listFile[i].getAbsolutePath();
// Get the name image file
FileNameStrings[i] = listFile[i].getName();
}
}
// Locate the GridView in gridview_main.xml
grid = (GridView) findViewById(R.id.gridview);
empty = (TextView) findViewById(R.id.empty);
empty.setText("You haven't saved any Pic yet...!");
grid.setEmptyView(empty);
// Pass String arrays to LazyAdapter Class
adapter = new GridViewAdapter(Downloaded.this, FilePathStrings, FileNameStrings);
// Set the LazyAdapter to the GridView
grid.setAdapter(adapter);
// Capture gridview item click
grid.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent i = new Intent(android.content.Intent.ACTION_VIEW);
//Don't Know what to Do Here :(
File n = new File(Environment.getExternalStorageDirectory()+ File.separator + "Sample/" + FileNameStrings);
i.setDataAndType(Uri.fromFile(n), "*/*");
startActivity(Intent.createChooser(i, "Choose an App"));
}
});
}
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId()){
case android.R.id.home:
onBackPressed();
break;
}
return true;
}
}
And here's the Adapter -
public class GridViewAdapter extends BaseAdapter {
// Declare variables
private Activity activity;
String[] filepath;
String[] filename;
Context mContext;
private static LayoutInflater inflater = null;
public GridViewAdapter( Context c ) {
mContext = c ;
}
public GridViewAdapter(Activity a, String[] fpath, String[] fname) {
activity = a;
this.filepath = fpath;
filename = fname;
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public int getCount() {
return this.filepath.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
if (convertView == null)
vi = inflater.inflate(R.layout.gridview_item, null);
// Locate the ImageView in gridview_item.xml
ImageView img = (ImageView) vi.findViewById(R.id.image);
// Set file name to the TextView followed by the position
TextView txt = (TextView) vi.findViewById(R.id.name);
// Decode the filepath with BitmapFactory followed by the position
// Set the decoded bitmap into ImageView
Glide.with(activity)
.load(filepath[position])
.into(img);
txt.setText(filename[position]);
return vi;
}
}
Any help would be appreciated... :)
Try like this
Intent i = new Intent(android.content.Intent.ACTION_VIEW);
// No need to set both Type & Data. if you set one itself enough
File n = new File(Environment.getExternalStorageDirectory()+ File.separator + "Sample/" + FileNameStrings);
i.setType(MimeTypeMap.getFileExtensionFromUrl(n.getAbsolutePath()));
startActivity(Intent.createChooser(i, "Choose an App"));
Updated
Try this for open file
MimeTypeMap myMime = MimeTypeMap.getSingleton();
Intent newIntent = new Intent(Intent.ACTION_VIEW);
String mimeType = myMime.getMimeTypeFromExtension(fileExt(file).substring(1));
newIntent.setDataAndType(Uri.fromFile(file),mimeType);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(newIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, "No handler for this type of file.", Toast.LENGTH_LONG).show();
}
private String fileExt(String url) {
if (url.indexOf("?") > -1) {
url = url.substring(0, url.indexOf("?"));
}
if (url.lastIndexOf(".") == -1) {
return null;
} else {
String ext = url.substring(url.lastIndexOf(".") + 1);
if (ext.indexOf("%") > -1) {
ext = ext.substring(0, ext.indexOf("%"));
}
if (ext.indexOf("/") > -1) {
ext = ext.substring(0, ext.indexOf("/"));
}
return ext.toLowerCase();
}
}
I use custom ArrayAdapter to populate listview.
I use Picasso to load images, before loading image I calculate height and width for each image. Thus dynamic ImageView has different height and width.
When I scroll up the listview, everything is smooth. But when I scroll the listview down, list starts to jump, when it comes to the rows with images.
I think, it caused by listview elements recycle, it forgets dynamic imageviews heights and produce this jumping effect when it recalculating imageviews again. I attach my dynamic imageview to Holder, but it doesn't help.
Part of my adapter looks like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ChatMessageElement el = list.get(position);
ViewHolder holder = null;
NewMessagesLabelHolder labelHolder = null;
if (convertView == null) {
convertView = el.getView(inflater, parent);
if (el.isMessage()) {
holder = new ViewHolder();
holder.messageLayout = (RelativeLayout) convertView.findViewById(R.id.message_container);
holder.messageContent = (LinearLayout) convertView.findViewById(R.id.message_content);
holder.bottomIndicator = (LinearLayout) convertView.findViewById(R.id.bottom_indicators);
holder.dateTextView = (TextView) convertView.findViewById(R.id.message_date);
holder.timeAgo = (TextView) convertView.findViewById(R.id.time_ago);
holder.nameTextView = (TextView) convertView.findViewById(R.id.user_name);
convertView.setTag(holder);
}
} else {
if (el.isMessage()) {
holder = (ViewHolder) convertView.getTag();
}
}
if (el.isMessage()) {
Message currentMessage = (Message) el;
drawMessage(holder, currentMessage, position);
}
return convertView;
}
private void drawMessage(ViewHolder holder, Message message, int position) {
String date = message.getCreatedAt();
String formattedDate = Helper.getInstance().formatDate("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "HH:mm", date);
String userName = message.getUserName();
holder.likesLabelImageView.setVisibility(View.GONE);
holder.likesCountTextView.setVisibility(View.GONE);
holder.nameTextView.setText(userName);
holder.dateTextView.setText(formattedDate);
if (message.isLiked()) {
holder.likesCountTextView.setText(Integer.toString(message.getLikesCount()));
holder.likesCountTextView.setVisibility(View.VISIBLE);
holder.likesLabelImageView.setVisibility(View.VISIBLE);
}
List<MessageComponent> messageComponentList;
messageComponentList = message.getMessageComponents();
drawMessageContent(holder, messageComponentList, message);
holder.nameTextView.setTag(position);
holder.avatarImageView.setTag(position);
holder.nameTextView.setOnClickListener(userClickListener);
holder.avatarImageView.setOnClickListener(userClickListener);
// hang empty onLingClickListener to display context menu when
// long click on whole message
holder.nameTextView.setOnLongClickListener(longClickListener);
holder.avatarImageView.setOnLongClickListener(longClickListener);
}
private void drawMessageContent(ViewHolder holder, final List<MessageComponent> messageComponentList, final Message msg) {
holder.messageContent.removeAllViewsInLayout();
int messageComponentListSize = messageComponentList.size();
for (final MessageComponent messageComponent : messageComponentList) {
messageComponentListSize--;
if (messageComponentListSize == 0)
iAmLast = true;
final String type = messageComponent.getType();
if (type.equals(MessageComponent.MESSAGE_COMPONENT_TEXT_TYPE)) {
TextView textView = new TextView(context);
textView.setText(messageComponent.getText());
setViewBackground(textView, msg);
//reset margins for texts, caused by margin changes for images
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.bottomIndicator.getLayoutParams();
params.setMargins(45, -82, 0, 0);
holder.bottomIndicator.setLayoutParams(params);
holder.messageContent.addView(textView);
}
if (type.equals(MessageComponent.MESSAGE_COMPONENT_IMAGE_TYPE)) {
ViewGroup.LayoutParams params = new ActionBar.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
thumbHeight
);
final RoundedImageView imageView = new RoundedImageView(context);
imageView.setPadding(20, 0, 20, 20);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setCornerRadius(15.0f);
mHandler.post(new Runnable() {
#Override
public void run() {
drawPreview(messageComponent, imageView);
}
});
imageView.setLayoutParams(params);
// hang empty onLingClickListener to display context menu when
// long click on whole message
imageView.setOnLongClickListener(longClickListener);
final RelativeLayout mediaContainer = new RelativeLayout(context);
mediaContainer.addView(imageView);
}
}
}
// Calculates restricted dimensions with a maximum of $goal_width by $goal_height
private ImageSize resize_dimensions(float goal_width, float goal_height, float width, float height) {
float ratio = Math.min(goal_width/width, goal_height/height);
int nwidth = Math.round(width*ratio);
int nheight = Math.round(height*ratio);
if(nwidth>nheight*2)
nheight = 400;
if(nheight>nwidth*2)
nwidth = 600;
ImageSize imageSize = new ImageSize(nwidth, nheight);
return imageSize;
}
private void drawPreview(MessageComponent messageComponent, final ImageView imageView) {
String type = messageComponent.getType();
String mediaPath = messageComponent.getMediaPath();
String thumbPath = messageComponent.getThumbPath();
String thumbUrl = messageComponent.getThumbUrl();
String videoThumbPath = messageComponent.getVideoThumbPath();
Uri uri = null;
if (type.equals(MessageComponent.MESSAGE_COMPONENT_IMAGE_TYPE)) {
if (!TextUtils.isEmpty(mediaPath)) {
uri = Uri.parse("file://" + mediaPath);
File file = new File(uri.getPath());
if (file.exists()) {
resizeAndLoadThumbnail(uri, imageView);
return;
}
}
if (!TextUtils.isEmpty(thumbPath)) {
uri = Uri.parse("file://" + mediaPath);
File file = new File(uri.getPath());
if (file.exists()) {
resizeAndLoadThumbnail(uri, imageView);
return;
}
}
if (thumbUrl != null) {
uri = Uri.parse(thumbUrl);
}
if (uri != null) {
resizeAndLoadThumbnail(uri, imageView);
return;
}
}
}
private void resizeAndLoadThumbnail(Uri uri, final ImageView imageView) {
Picasso.with(context).load(uri).into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
ImageSize imgSize = resize_dimensions(900, 900, width, height);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
imgSize.width,
imgSize.height
);
imageView.setLayoutParams(params);
imageView.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
Here is how I set up the list view and how I get the image by downloading it.
Some variable explanation :
The PostItem is the model object that contain the data for a listview item
The ImageLoader is the async task class to download the image by getting the image url from PostItem
The problem are , the ordering of the image in the listview is incorrect , for example, the image should appear in 1st is appear in both 1st , 4th, and if I scroll , the display pattern change as well.
Also, I find the image are download again if I scroll, even I have check the imageView whether has drawable
Thanks for helping.
====================================================
Here is how I generate the listview:
static class ViewHolderItem {
TextView name;
TextView date;
ImageView img;
TextView msg;
TextView count;
ImageView likeBtn;
ImageView commentBtn;
ImageView shareBtn;
}
private class MyPostAdapter extends ArrayAdapter<PostItem> {
#Override
public boolean isEnabled(int position) {
return false;
}
public MyPostAdapter(Context context, int resource, List<PostItem> items) {
super(context, resource, items);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolderItem viewHolder;
if (v == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.post_item, parent, false);
viewHolder = new ViewHolderItem();
viewHolder.name = (TextView) v.findViewById(R.id.postName);
viewHolder.date = (TextView) v.findViewById(R.id.postDate);
viewHolder.img = (ImageView) v.findViewById(R.id.postImg);
viewHolder.msg = (TextView) v.findViewById(R.id.postMsg);
viewHolder.count = (TextView) v.findViewById(R.id.count);
viewHolder.likeBtn = (ImageView) v.findViewById(R.id.likeBtn);
viewHolder.commentBtn = (ImageView) v.findViewById(R.id.commentBtn);
viewHolder.shareBtn = (ImageView) v.findViewById(R.id.shareBtn);
v.setTag(viewHolder);
} else {
viewHolder = (ViewHolderItem) convertView.getTag();
}
final PostItem post = getItem(position);
if (post != null) {
viewHolder.name.setText(post.name);
try {
c.setTime(sdf.parse(post.createDate));
} catch (ParseException e) {
e.printStackTrace();
}
relative_date = DateUtils.getRelativeDateTimeString (ctx, c.getTimeInMillis() , DateUtils.MINUTE_IN_MILLIS,DateUtils.WEEK_IN_MILLIS, 0).toString();
viewHolder.date.setText(relative_date);
viewHolder.msg.setText(post.txtMsg);
viewHolder.count.setText(post.likeCount + " " + getString(R.string.pro_like) + " " + post.commentCount + " " + getString(R.string.reply));
if (post.isLike) {
viewHolder.likeBtn.setImageResource(R.drawable.like);
} else {
viewHolder.likeBtn.setImageResource(R.drawable.before_like);
}
if (!post.imageURL.equals("null") && viewHolder.img.getDrawable() == null ) {
new ImageLoader(ctx).execute(viewHolder.img,Constant.comment_imageFolder + post.imageURL);
} else {
viewHolder.img.setImageDrawable(null);
}
viewHolder.likeBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new APIManager("like", ctx, Constant.likeAPI + "/"
+ post.commentID + "/" + userID, jsonListener,
getResources().getString(R.string.update_data));
}
});
viewHolder.commentBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ArrayList<PostItem> filterReplyList = new ArrayList<PostItem>();
Intent i = new Intent(ctx, ReplyActivity.class);
i.putExtra("commentID", post.commentID);
// get reply list
for (PostItem reply : replyItemList) {
if (reply.postID.equals(post.commentID)
|| reply.commentID.equals(post.commentID)) {
filterReplyList.add(reply);
}
}
i.putExtra("replyItemList", filterReplyList);
startActivityForResult(i, 0);
}
});
viewHolder.shareBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
String data = "date: " + post.createDate + "\nmsg:" + post.txtMsg;
sendIntent.putExtra(Intent.EXTRA_TEXT, data);
sendIntent.setType("text/plain");
startActivity(sendIntent);
}
});
}
return v;
}
}
And Here is the imageloader, take the imageview, url as input and put the bitmap in the imageview
public class ImageLoader extends AsyncTask<Object, Void, Bitmap> {
private static String TAG = "ImageLoader";
private InputStream input;
private ImageView view;
private ProgressBar loadingIcon;
private ListView myListView;
private String imageURL;
private Context ctx;
public ImageLoader(Context _ctx) {
ctx = _ctx;
}
#Override
protected Bitmap doInBackground(Object... params) {
try {
view = (ImageView) params[0];
// handle Chinese characters in file name
// String[] imgUrlArray = ((String) params[1]).split("/");
// String fileName = imgUrlArray[imgUrlArray.length - 1];
// String newfileName = URLEncoder.encode(fileName, "utf-8");
// imageURL = ((String) params[1]).replace(fileName, newfileName);
imageURL = ((String) params[1]);
if (params.length > 2 && (ProgressBar) params[2] != null)
loadingIcon = (ProgressBar) params[2];
URL url = new URL(imageURL);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setDoInput(true);
connection.connect();
input = connection.getInputStream();
final BitmapFactory.Options options = new BitmapFactory.Options();
BufferedInputStream bis = new BufferedInputStream(input, 4*1024);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
options.inJustDecodeBounds = true;
options.inSampleSize = 2;
options.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
if (input != null)
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
protected void onPostExecute(Bitmap result) {
if (result != null && view != null) {
if (loadingIcon != null)
loadingIcon.setVisibility(View.GONE);
view.setVisibility(View.VISIBLE);
view.setImageBitmap(result);
}
}
Updated code (implement volley library):
static class ViewHolderItem {
TextView name;
TextView date;
NetworkImageView img;
TextView msg;
TextView count;
ImageView likeBtn;
ImageView commentBtn;
ImageView shareBtn;
}
private class MyPostAdapter extends ArrayAdapter<PostItem> {
#Override
public boolean isEnabled(int position) {
return false;
}
public MyPostAdapter(Context context, int resource, List<PostItem> items) {
super(context, resource, items);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolderItem viewHolder;
if (v == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.post_item, parent, false);
viewHolder = new ViewHolderItem();
viewHolder.name = (TextView) v.findViewById(R.id.postName);
viewHolder.date = (TextView) v.findViewById(R.id.postDate);
viewHolder.img = (NetworkImageView) v.findViewById(R.id.postImg);
viewHolder.msg = (TextView) v.findViewById(R.id.postMsg);
viewHolder.count = (TextView) v.findViewById(R.id.count);
viewHolder.likeBtn = (ImageView) v.findViewById(R.id.likeBtn);
viewHolder.commentBtn = (ImageView) v.findViewById(R.id.commentBtn);
viewHolder.shareBtn = (ImageView) v.findViewById(R.id.shareBtn);
v.setTag(viewHolder);
} else {
viewHolder = (ViewHolderItem) convertView.getTag();
}
final PostItem post = getItem(position);
if (post != null) {
viewHolder.name.setText(post.name);
try {
c.setTime(sdf.parse(post.createDate));
} catch (ParseException e) {
e.printStackTrace();
}
relative_date = DateUtils.getRelativeDateTimeString (ctx, c.getTimeInMillis() , DateUtils.MINUTE_IN_MILLIS,DateUtils.WEEK_IN_MILLIS, 0).toString();
viewHolder.date.setText(relative_date);
viewHolder.msg.setText(post.txtMsg);
viewHolder.count.setText(post.likeCount + " " + getString(R.string.pro_like) + " " + post.commentCount + " " + getString(R.string.reply));
if (post.isLike) {
viewHolder.likeBtn.setImageResource(R.drawable.like);
} else {
viewHolder.likeBtn.setImageResource(R.drawable.before_like);
}
if (!post.imageURL.equals("null")) {
viewHolder.img.setImageUrl(Constant.comment_imageFolder + post.imageURL, mImageLoader);
}
viewHolder.likeBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new APIManager("like", ctx, Constant.likeAPI + "/"
+ post.commentID + "/" + userID, jsonListener,
getResources().getString(R.string.update_data));
}
});
viewHolder.commentBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ArrayList<PostItem> filterReplyList = new ArrayList<PostItem>();
Intent i = new Intent(ctx, ReplyActivity.class);
i.putExtra("commentID", post.commentID);
// get reply list
for (PostItem reply : replyItemList) {
if (reply.postID.equals(post.commentID)
|| reply.commentID.equals(post.commentID)) {
filterReplyList.add(reply);
}
}
i.putExtra("replyItemList", filterReplyList);
startActivityForResult(i, 0);
}
});
viewHolder.shareBtn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
String data = "date: " + post.createDate + "\nmsg:" + post.txtMsg;
sendIntent.putExtra(Intent.EXTRA_TEXT, data);
sendIntent.setType("text/plain");
startActivity(sendIntent);
}
});
}
return v;
}
For the task you are trying to do I would strongly recommend you to use Volley library.
Read from here
All you need to do is as below
mRequestQueue = Volley.newRequestQueue(context);
mImageLoader = new ImageLoader(mRequestQueue, new BitmapLruCache());
mImageView.setImageUrl(BASE_URL + item.image_url, mImageLoader);
Where mImageView is com.android.volley.NetworkImageView instead of a regular ImageView.
Volley takes care of maintaining the cache and the ordering of the images.
if you scroll the listview you will get back recycled convertview, it is not null but it has incorrect imageview. convertView is a view thats created and recycled through scrolling the list. this view makes GC be called less and also save memory for you. it first assigned by your earliest items of list. after you scroll the list, for example item one of list disappears and you see item 15 the convertView of item one is passed again to you. in this time it is not null and it holdes the reference of last imageview, the imageview of item 1.
so this is your problem, you skipped assigning correct imageview to your viewHolder.img.
Ok, what should you do?
the best thing you can do is create in memory cache that holds your downloaded imageview by their URLs as keys of the cache. in getview you check the cache, if it has your URL of current imageview position read from it and set it to viewHolder.img else download the image from internet.
Golden rule is:
ALWAYS OVERWRITE VIEWHOLDER VALUES WITH VALUES OF YOUR ITEM AT INDEX POSITON THAT
GETVIEW PASSES TO YOU
how to create cache? look at Example LRU cache at
http://developer.android.com/training/volley/request.html
and if you want you can also use volley library instead.
if (!post.imageURL.equals("null") && viewHolder.img.getDrawable() == null ) {
new ImageLoader(ctx).execute(viewHolder.img,Constant.comment_imageFolder + post.imageURL);
}
I am guessing that the problem lies here. What happens when you get a recycled view which already has an image from the previous view it was used for? That explains why the image appears in both 1st and 4th position and the change in the display pattern when you scroll.
Get the point? Remove the viewHolder.img.getDrawable() == null and try. See if that helps.
I hope the title is not mis-leading. I am trying to implement onRetainNonConfigurationInstance() of an AsyncTask of loading images and am getting this error "Java.lang.RuntimeException:Unable to retain Activity". Here is my code for onRetainNonConfigurationInstance():
public Object onRetainNonConfigurationInstance(){
//final ListView listview = list;
final int count = list.getChildCount();
final LoadedImage[] mylist = new LoadedImage[count];
for(int i = 0; i < count; i++){
final ImageView v = (ImageView)list.getChildAt(i); // getting error here
mylist[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
}
return mylist;
}
Here is the Logcat:
05-18 08:43:15.385: E/AndroidRuntime(28130): java.lang.RuntimeException: Unable to retain activity {com.MyApps.ImageGen/com.MyApps.ImageGen.Wallpapers}: java.lang.ClassCastException: android.widget.LinearLayout
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:2989)
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3100)
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3216)
05-18 08:43:15.385: E/AndroidRuntime(28130): at android.app.ActivityThread.access$1600(ActivityThread.java:132)
Here is my layout with the ListView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:listSelector="#android:color/transparent" >
</ListView>
</LinearLayout>
Here is how I setup the ListView:
private void setupViews() {
list = (ListView)findViewById(android.R.id.list);
list.setDivider(null);
list.setDividerHeight(0);
imageAdapter = new ImageAdapter(getApplicationContext());
list.setAdapter(imageAdapter);
list.setOnItemClickListener(this);
}
my async task and Item Adapter:
class LoadImagesFromSDCard extends AsyncTask<Object, LoadedImage, Object> {
#Override
protected Object doInBackground(Object... params) {
Bitmap bitmap = null;
Bitmap newbitmap = null;
Uri uri = null;
String [] img = {MediaStore.Images.Media._ID};
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, img, null, null, null);
} else {
// cursor = getContentResolver().query(MediaStore.Images.Media.INTERNAL_CONTENT_URI, img, null, null, null);
inInternalStorage = true;
}
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int size = cursor.getCount();
if(size == 0){
//Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();
//System.out.println("size equals zero");
Wallpaper.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();
}
});
return params;
}else {
}
for(int i = 0; i < size; i++){
cursor.moveToPosition(i);
int ImageId = cursor.getInt(column_index);
if(inInternalStorage == true){
uri = Uri.withAppendedPath(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "" + ImageId);
}else {
uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + ImageId);
}
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Log.i(TAG, "imageheight = " + imageHeight);
Log.i(TAG, "imagewidth = " + imageWidth);
Log.i(TAG, "imageType = " + imageType);
//options.inSampleSize=4;
options.inSampleSize = calculateInSampleSize(options, imageWidth, imageHeight);
options.inJustDecodeBounds = false;
//bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);
if(bitmap != null){
newbitmap = Bitmap.createScaledBitmap(bitmap, 180, 180, true);
bitmap.recycle();
}
if(newbitmap != null){
publishProgress(new LoadedImage(newbitmap));
}
}catch(IOException e){
}
//cursor.close();
}
cursor.close();
return null;
}
#Override
public void onProgressUpdate(LoadedImage... value){
addImage(value);
}
#Override
protected void onPostExecute(Object result) {
setProgressBarIndeterminateVisibility(false);
}
}
private static class LoadedImage {
Bitmap mBitmap;
LoadedImage(Bitmap bitmap) {
mBitmap = bitmap;
}
public Bitmap getBitmap() {
return mBitmap;
}
}
/*Image Adapter to populate grid view of images*/
public class ImageAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();
public ImageAdapter(Context context){
this.mContext = context;
}
public void addPhotos(LoadedImage photo){
photos.add(photo);
}
#Override
public int getCount() {
return photos.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView image;
ViewHolder holder;
if(convertView == null){
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//convertView = inflater.inflate(R.layout.image_list,null);
convertView = inflater.inflate(R.layout.media_view, null);
holder = new ViewHolder();
holder.image = (ImageView)convertView.findViewById(R.id.media_image_id);
//holder.item_name = (TextView)convertView.findViewById(R.id.media_image_id);
convertView.setTag(holder);
} else{
holder = (ViewHolder)convertView.getTag();
}
//holder.image.setLayoutParams(new GridView.LayoutParams(100, 100));
//String item_name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));
holder.image.setImageBitmap(photos.get(position).getBitmap());
//holder.item_name.setText(item_name);
return convertView;
}
}
static class ViewHolder {
ImageView image;
TextView item_name;
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Cursor cursor = null;
int image_column_index = 0;
String[] proj = {MediaStore.Images.Media.DATA};
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)){
cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,proj,null, null,null);
}else{
cursor = managedQuery(MediaStore.Images.Media.INTERNAL_CONTENT_URI,proj,null, null,null);
}
cursor.moveToPosition(position);
image_column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
String info = cursor.getString(image_column_index);
Intent imageviewer = new Intent(getApplicationContext(), ViewImage.class);
imageviewer.putExtra("pic_name", info);
startActivity(imageviewer);
cursor.close();
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 2;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
/*final int REQUIRED_SIZE = 180;
int scale = 1;
if(reqWidth > REQUIRED_SIZE || reqHeight > REQUIRED_SIZE){
scale = (int)Math.pow(2, (int)Math.round(Math.log(REQUIRED_SIZE/(double)Math.max(reqHeight, reqWidth)) / Math.log(0.5)));
}*/
return inSampleSize;
}
other Methods within Async Task:
private void loadImages() {
final Object data = getLastNonConfigurationInstance();
if(data == null){
new LoadImagesFromSDCard().execute();
}else {
final LoadedImage[] photos = (LoadedImage[])data;
if(photos.length == 0){
new LoadImagesFromSDCard().execute();
}
for(LoadedImage photo:photos){
addImage(photo);
}
}
}
private void addImage(LoadedImage... value) {
for(LoadedImage photo: value){
imageAdapter.addPhotos(photo);
imageAdapter.notifyDataSetChanged();
}
}
I am assuming I should first get the root element before I get the ListView from the logcat error, but I don't know how, I can't seem to find a suitable method from the documentation.
P.S: I am using an Activity and not a ListActivity.
The exception tells you that you try to do an invalid cast. My guess is that your row layout isn't a simple ImageView like you assume with the cast in the onRetainNonConfigurationInstance(), instead your row layout probably has a parent Linearlayout that wraps the ImageView(and other views?!). When you call the method getChildAt you get that parent Linearlayout which you try to cast to a ImageView. Instead you should do this:
//...
for(int i = 0; i < count; i++){
LinearLayout parent = (LinearLayout)list.getChildAt(i);
final ImageView v = (ImageView) parent.findViewById(R.id.the_id_of_the_imageview_from_theRow_layout);
mylist[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
}
Note: The getChildAt method returns the rows views for the visible rows only so if you try to access the View for a row that isn't currently visible you'll most likely end up with a NullPointerException(you should get the drawables directly from the adapter and not from parsing all the rows Views).
Edit based on the comments:
If I understood your problem, the behavior you see it's normal. You didn't say how you ended up getting the bitmaps in the onRetainNonConfigurationInstance but my guess is that you just save the images from the ListView rows that are currently visible to the user. When it's time to restore the adapter with the images from getLastNonConfigurationInstance() you end up getting only those images which were previously visible. This will get worse if you rotate the phone again as, most likely, there are fewer images visible in landscape orientation then in the portrait orientation.
I don't know if this will work but you could try to modify the LoadedImage class to store the id of the image from MediaStore.Images.Media. When it's time to save the configuration, stop the LoadImagesFromSDCard task and nullify all the images(remove the Bitmap to which LoadedImage points)from the LoadedImages ArrayList(in the adapter) except the ones that are currently visible to the user. Send the photos ArrayList from your adapter through
onRetainNonConfigurationInstance.
When it's time to restore the adapter set the photos ArrayList of your new adapter to the one you got back and put the ListView to the position of the visible images(so the user sees the sent images). In the getView method of your adapter you'll put a default image(like loading...) when you have a null value for the Bitmap in the associated LoadedImage class. Start a new LoadImagesFromSDCard task to get the images and parse them again. When you get the id from MediaStore.Images.Media you'll check if a LoadedImage with this id exists in the adapter and if it has a Bitmap set to it. If it doesn't exist(or it doesn't have a Bitmap associated) then load the image and put it in the adapter, if not the image already exists and move to the next one.
I don't know if the solution above will work or if it's efficient. I would recommend you to modify the code and load images on demand instead of loading all up in a big task.
I have a gallery that is displaying thumbnail images in custom directories. The gallery displays fine, but I am unable to open the full image by clicking the thumbnail. My [non functioning] click listener is below
try {
if (LoadImageFiles() == true) {
GridView imgGallery = (GridView) findViewById(R.id.gallery);
final ImageAdapter ia = new ImageAdapter(PersonMedia.this);
imgGallery.setAdapter(ia);
// Set up a click listener
imgGallery.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
String imgPath = paths.get(position);
Intent intent = new Intent(getApplicationContext(), ViewImage.class);
intent.putExtra("filename", imgPath);
startActivity(intent);
}
});
}
} catch (Exception ex) {
Log.e("PersonMedia.LoadPictures", ex.getMessage());
}
Here is how the gallery is populated
//Declare a module level hashtable
private Hashtable<Integer, String> paths;
private boolean LoadImageFiles() {
try{
mySDCardImages = new Vector<ImageView>();
paths = new Hashtable<Integer, String>();
fileCount = 0;
sdDir = new File(imageDirectory);
sdDir.mkdir();
if (sdDir.listFiles() != null)
{
File[] sdDirFiles = sdDir.listFiles();
if (sdDirFiles.length > 0)
{
for (File singleFile : sdDirFiles)
{
Bitmap bmap = decodeFile(singleFile);
BitmapDrawable pic = new BitmapDrawable(bmap);
ImageView myImageView = new ImageView(PersonMedia.this);
myImageView.setImageDrawable(pic);
myImageView.setId(mediaCount);
paths.put(fileCount, singleFile.getAbsolutePath());
mySDCardImages.add(myImageView);
mediaCount++;
fileCount ++;
}
}
}
}
catch(Exception ex){ Log.e("LoadImageFiles", ex.getMessage()); }
return (fileCount > 0);
}
I was able to resolve this by using a hashtable to store the position and path of the images when the thumbnails are loaded. Below are the 2 pertinent code snippets
//Where the gallery is populated and the onclick is defined
private void PopulateGallery() {
try {
if (LoadImageFiles() == true) {
GridView imgGallery = (GridView) findViewById(R.id.gallery);
imgGallery.setAdapter(new ImageAdapter(PersonMedia.this));
// Set up a click listener
imgGallery.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
String imgPath = paths.get(position);
Intent intent = new Intent(getApplicationContext(), ViewImage.class);
intent.putExtra("filename", imgPath);
startActivity(intent);
}
});
}
} catch (Exception ex) {
Log.e("PersonMedia.LoadPictures", ex.getMessage());
}
}
//Where the images are loaded. You'll need to create a module level hashtable
private Hashtable<Integer, String> paths;
private boolean LoadImageFiles() {
try{
mySDCardImages = new Vector<ImageView>();
paths = new Hashtable<Integer, String>();
fileCount = 0;
sdDir = new File(imageDirectory);
sdDir.mkdir();
if (sdDir.listFiles() != null)
{
File[] sdDirFiles = sdDir.listFiles();
if (sdDirFiles.length > 0)
{
for (File singleFile : sdDirFiles)
{
Bitmap bmap = decodeFile(singleFile);
BitmapDrawable pic = new BitmapDrawable(bmap);
ImageView myImageView = new ImageView(PersonMedia.this);
myImageView.setImageDrawable(pic);
myImageView.setId(mediaCount);
paths.put(fileCount, singleFile.getAbsolutePath());
mySDCardImages.add(myImageView);
mediaCount++;
fileCount ++;
}
}
}
}
catch(Exception ex){ Log.e("LoadImageFiles", ex.getMessage()); }
return (fileCount > 0);
}
Do your images have extensions, such as .jpg or .png? I'm not quite sure but it looks like you are looking for an image that is string version of your personId, without any file extension.
Please correct me if I'm wrong.
Also please post more details, such as the errors you are getting.
Updated answer below.
Use View.setTag(Object), when you are adding items to your ListView (I am guessing). You can call something like the following.
view.setTag("imgFile.jpg");
And then from inside of your onClickListener, just do this:
String img = (String) getTag();