I have tried to search for solution, but I still can’t solve it and my R.java can’t generate R.id.picture. May I know where I did wrong?
/**
* PicSelectActivity presents image gallery - user can select new images to
* Display within scrolling thumbnail gallery - user can select individual image
* To display at larger size
*
* Sue Smith Mobiletuts+ Tutorial - Importing and Displaying Images with the
* Android Gallery June 2012
*/
public class FxPhoto extends Activity {
// variable for selection intent
private final int PICKER = 1;
// variable to store the currently selected image
private int currentPic = 0;
// adapter for gallery view
private PicAdapter imgAdapt;
// gallery object
private Gallery picGallery;
// image view for larger display
private ImageView picView;
/**
* instantiate the interactive gallery
*/
#Override
public void onCreate(Bundle savedInstanceState) {
/*
* When the user clicks a thumbnail in the gallery it is displayed at
* larger size
*
* When the user long-clicks a thumbnail they are taken to their chosen
* image selection activity, either the Android image gallery or a file
* manager application - on returning the chosen image is displayed
* within the thumbnail gallery and larger image view
*/
// call superclass method and set main content view
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// get the large image view
picView = (ImageView) findViewById(**R.id.picture**);
// get the gallery view
picGallery = (Gallery) findViewById(**R.id.gallery**);
// create a new adapter
imgAdapt = new PicAdapter(this);
// set the gallery adapter
picGallery.setAdapter(imgAdapt);
// set long click listener for each gallery thumbnail item
picGallery.setOnItemLongClickListener(new OnItemLongClickListener() {
// handle long clicks
public boolean onItemLongClick(AdapterView<?> parent, View v,
int position, long id) {
// update the currently selected position so that we assign the
// imported bitmap to correct item
currentPic = position;
// take the user to their chosen image selection app (gallery or
// file manager)
Intent pickIntent = new Intent();
pickIntent.setType("image/*");
pickIntent.setAction(Intent.ACTION_GET_CONTENT);
// we will handle the returned data in onActivityResult
startActivityForResult(
Intent.createChooser(pickIntent, "Select Picture"),
PICKER);
return true;
}
});
// set the click listener for each item in the thumbnail gallery
picGallery.setOnItemClickListener(new OnItemClickListener() {
// handle clicks
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// set the larger image view to display the chosen bitmap
// calling method of adapter class
picView.setImageBitmap(imgAdapt.getPic(position));
}
});
}
/**
* Base Adapter subclass creates Gallery view - provides method for adding
* new images from user selection - provides method to return bitmaps from
* array
*
*/
public class PicAdapter extends BaseAdapter {
// use the default gallery background image
int defaultItemBackground;
// gallery context
private Context galleryContext;
// array to store bitmaps to display
private Bitmap[] imageBitmaps;
// placeholder bitmap for empty spaces in gallery
Bitmap placeholder;
// constructor
public PicAdapter(Context c) {
// instantiate context
galleryContext = c;
// create bitmap array
imageBitmaps = new Bitmap[10];
// decode the placeholder image
placeholder = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
// set placeholder as all thumbnail images in the gallery initially
for (int i = 0; i < imageBitmaps.length; i++)
imageBitmaps[i] = placeholder;
// get the styling attributes - use default Andorid system resources
TypedArray styleAttrs = galleryContext
.obtainStyledAttributes(R.**styleable**.PicGallery);
// get the background resource
defaultItemBackground = styleAttrs.getResourceId(
R.**styleable**.PicGallery_android_galleryItemBackground, 0);
// recycle attributes
styleAttrs.recycle();
}
// BaseAdapter methods
// return number of data items i.e. bitmap images
public int getCount() {
return imageBitmaps.length;
}
// return item at specified position
public Object getItem(int position) {
return position;
}
// return item ID at specified position
public long getItemId(int position) {
return position;
}
// get view specifies layout and display options for each thumbnail in
// the gallery
public View getView(int position, View convertView, ViewGroup parent) {
// create the view
ImageView imageView = new ImageView(galleryContext);
// specify the bitmap at this position in the array
imageView.setImageBitmap(imageBitmaps[position]);
// set layout options
imageView.setLayoutParams(new Gallery.LayoutParams(300, 200));
// scale type within view area
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
// set default gallery item background
imageView.setBackgroundResource(defaultItemBackground);
// return the view
return imageView;
}
// custom methods for this app
// helper method to add a bitmap to the gallery when the user chooses
// one
public void addPic(Bitmap newPic) {
// set at currently selected index
imageBitmaps[currentPic] = newPic;
}
// return bitmap at specified position for larger display
public Bitmap getPic(int posn) {
// return bitmap at posn index
return imageBitmaps[posn];
}
}
/**
* Handle returning from gallery or file manager image selection - import
* the image bitmap
*/
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
// check if we are returning from picture selection
if (requestCode == PICKER) {
// the returned picture URI
Uri pickedUri = data.getData();
// declare the bitmap
Bitmap pic = null;
// declare the path string
String imgPath = "";
// retrieve the string using media data
String[] medData = { MediaStore.Images.Media.DATA };
// query the data
Cursor picCursor = managedQuery(pickedUri, medData, null, null,
null);
if (picCursor != null) {
// get the path string
int index = picCursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
picCursor.moveToFirst();
imgPath = picCursor.getString(index);
} else
imgPath = pickedUri.getPath();
// if and else handle both choosing from gallery and from file
// manager
// if we have a new URI attempt to decode the image bitmap
if (pickedUri != null) {
// set the width and height we want to use as maximum
// display
int targetWidth = 600;
int targetHeight = 400;
// sample the incoming image to save on memory resources
// create bitmap options to calculate and use sample size
BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
// first decode image dimensions only - not the image bitmap
// itself
bmpOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgPath, bmpOptions);
// work out what the sample size should be
// image width and height before sampling
int currHeight = bmpOptions.outHeight;
int currWidth = bmpOptions.outWidth;
// variable to store new sample size
int sampleSize = 1;
// calculate the sample size if the existing size is larger
// than target size
if (currHeight > targetHeight || currWidth > targetWidth) {
// use either width or height
if (currWidth > currHeight)
sampleSize = Math.round((float) currHeight
/ (float) targetHeight);
else
sampleSize = Math.round((float) currWidth
/ (float) targetWidth);
}
// use the new sample size
bmpOptions.inSampleSize = sampleSize;
// now decode the bitmap using sample options
bmpOptions.inJustDecodeBounds = false;
// get the file as a bitmap
pic = BitmapFactory.decodeFile(imgPath, bmpOptions);
// pass bitmap to ImageAdapter to add to array
imgAdapt.addPic(pic);
// redraw the gallery thumbnails to reflect the new addition
picGallery.setAdapter(imgAdapt);
// display the newly selected image at larger size
picView.setImageBitmap(pic);
// scale options
picView.setScaleType(ImageView.ScaleType.FIT_CENTER);
}
}
}
// superclass method
super.onActivityResult(requestCode, resultCode, data);
}
}
i also encounter that problem,
look at your packages
if you found something like import android.R;
remove that line.
This works.
Step 1: Download http://www.androidpeople.com/wp-content/uploads/2010/05/GalleryImageview.zip
Step 2: Run and Update here with working version
Related
I am creating an app that has a file directory that contains pictures, videos, pdfs, etc. I am currently working on displaying thumbnails for pictures. I am using the RecyclerView and ViewHolder to display list items that each represent a photo item. I then use an AsyncTask to download the Bitmaps one at a time and store them in a Hashmap. Everything works fine except when I scroll down in a large list of photos very quickly. The placeholder image for random items at the bottom of the list are replaced with thumbnails that have already been loaded at the top of the list. When the background thread reaches the image at the bottom, then the correct image replaces the wrong image. After all the thumbnails are loaded then everything works as intended.
Here is the code for the AsyncTask. I think the problem has to do with the position integer I am passing into the constructor. The position variable represents the position in the Adapter. Maybe there is a way to make sure the image is loading the placeholder image I have in onPreExecute()?
/**
* AsyncTask to download the thumbnails in the RecyclerView list.
*/
private class CreateThumbnail extends AsyncTask<Void, Void, android.graphics.Bitmap> {
// ******
// FIELDS
// ******
private ImageView mPreviewInstance;
private File mFile;
private RelativeLayout.LayoutParams lp;
private FileHolder mFileHolder;
private int mPosition;
private UUID mId;
private FolderFile mFolderFile;
// ***********
// Constructor
// ***********
/**
* #param holder - ViewHolder passed for the list item.
* #param position - position in the Adapter.
* #param id - id for list item stored in database.
*/
private CreateThumbnail(FileHolder holder, int position, UUID id) {
mPosition = position;
mFileHolder = holder;
mPreviewInstance = mFileHolder.mImagePreview;
mId = id;
mFolderFile = FolderFileLab.get(getContext()).getFolderFile(mId);
}
// ****************
// OVERRIDE METHODS
// ****************
#Override
protected void onPreExecute() {
}
#Override
protected Bitmap doInBackground(Void... params) {
FolderFileLab lab = FolderFileLab.get(getContext());
if (!lab.getCurrentMap().containsKey(mId)) {
mFile = lab.getPhotoFile(mFolderFile);
// Create Bitmap (Biggest use of memory and reason this background thread exists)
Bitmap bitmap = PictureUtils.getScaledBitmap(
mFile.getPath(), getActivity());
// Scales Bitmap down for thumbnail.
Bitmap scaledBitmap;
if (bitmap.getWidth() >= bitmap.getHeight()) {
scaledBitmap = Bitmap.createBitmap(bitmap, bitmap.getWidth() / 2
- bitmap.getHeight() / 2,
0, bitmap.getHeight(), bitmap.getHeight());
} else {
scaledBitmap = Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() / 2
- bitmap.getWidth() / 2,
bitmap.getWidth(), bitmap.getWidth());
}
// Cache bitmap
HashMap<UUID, Bitmap> map = lab.getCurrentMap();
map.put(mId, scaledBitmap);
lab.updateMap(map);
return scaledBitmap;
} else {
// If Hashmap already contains the id get the Bitmap.
return lab.getCurrentMap().get(mId);
}
}
#Override
protected void onPostExecute(Bitmap bitmap) {
// Checks to see if the bitmap is still displayed in the list. If not nothing happens.
// If it is then it displays the image.
if (mPreviewInstance.getVisibility() == View.VISIBLE && mFileHolder.getPosition()
== mPosition && bitmap != null) {
// Formatting for thumbnail
lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout
.LayoutParams.WRAP_CONTENT);
lp.setMargins(7, 7, 7, 0);
// Displaying thumbnail on UI thread.
mPreviewInstance.setLayoutParams(lp);
mPreviewInstance.setBackground(null);
mPreviewInstance.setImageBitmap(bitmap);
}
}
}
Here is some of the relevant Adapter code where the AsyncTask is started.
#Override
public void onBindViewHolder(FileHolder holder, int position) {
FolderFile file = mFiles.get(position);
holder.bindFile(file);
if (file.isPhoto()) {
createThumbnail = new CreateThumbnail(holder, position,file.getId());
createThumbnail.execute();
}
}
Figured it out!
I added code to change the photo to the placeholder image after every bind. This is what I changed in my adapter.
#Override
public void onBindViewHolder(FileHolder holder, int position) {
FolderFile file = mFiles.get(position);
holder.bindFile(file);
if (file.isPhoto()) {
Drawable placeholder = getResources().getDrawable(R.mipmap.picture_blu);
holder.mImagePreview.setBackground(placeholder);
holder.mImagePreview.setImageBitmap(null);
createThumbnail = new CreateThumbnail(holder, position, file.getId());
createThumbnail.execute();
}
}
Your views are recycled, so by the time the async task finishes, the imageView has been reused and has a new image assigned to it.
What you can do is assign to the imageView a tag that is the file name of the file you are trying to load into it. You keep track of that same file name in the async task. Then in your AsyncTask, in onPostExecute, you check if the tag the imageView has is the same file name that you just loaded. If it is, you go ahead and set the bitmap to the imageView. If it is not, then the view has been recycled and you simply drop the Bitmap you just created; another async task will be loading the right bitmap.
So basically, a user click a button and it grabs an image from the gallery. Then that image is sent to another activity to be displayed. This is my first activity where I grab the image.
private void grabImage()
{
Intent imageGetter = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(imageGetter, RESULT_LOAD_IMAGE);
}
#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)
{
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};//Array size of 1, and we put in a string
Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
user_image_path = cursor.getString(columnIndex);//here we have our image path.
cursor.close();
ImageView imageView = (ImageView) findViewById(R.id.imageView);
imageView.setImageBitmap(BitmapFactory.decodeFile(user_image_path));
}
Intent theIntent = new Intent(this,CardModel.class);
theIntent.putExtra("imagePath", user_image_path);
}
Now this is my second activity that tries to display that image.
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.card_model_layout);
String grabImagePath = getIntent().getStringExtra("imagePath");
ImageView img = (ImageView) findViewById(R.id.userImage);
img.setImageBitmap(BitmapFactory.decodeFile(grabImagePath));
}
I keep getting OutOfMemoryError error and I don't wanna add:
android:largeHeap="true" to solve the problem.
Can someone give me a clean cut example (with all the code) of how to properly pass an image (using its string path) between two activities. I'm sure a lot of developers could benefit from this. Thanks!
Also is there a way to do this without calling onActivityResult and instead just make you're own method and put the code in there and call that in the onCreate() method.
The fact is-
Given that you are working with limited memory, ideally you only want
to load a lower resolution version in memory. The lower resolution
version should match the size of the UI component that displays it. An
image with a higher resolution does not provide any visible benefit,
but still takes up precious memory and incurs additional performance
overhead due to additional on the fly scaling.
[Reference Link : http://developer.android.com/training/displaying-bitmaps/load-bitmap.html]
That's why you need to scale down images . Hope, following code will help you !
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class BitmapUtility {
public static Bitmap decodeSampledBitmapFromResource(String path,int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path,options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
private 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 = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
You can use above code as follows:
Instead of using:
imageView.setImageBitmap(BitmapFactory.decodeFile(user_image_path));
you can use:
imageView.setImageBitmap(BitmapUtilty.decodeSampledBitmapFromResource("path/to/image",300,400)
With images (especially from sources you don't control, like internet or user data) you cannot just load them. It may be huge. Previous answer shows how to do it right.
Another thing to take care about is recycling native memory occupied by image data. For some reason it does not get recycled automatically or does it too late, when OOM is already here. To do this right one should maintain reference counting, which requires some work with extending couple classes. This is how I solved the issue. I think I used some code from android tutorial and extended it a bit. Anyways, it was couple years ago so I'm not really sure ) Here it is.
package com.companyname.myapp.ui.cache;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import com.companyname.myapp.MyApp;
import com.companyname.myapp.BuildConfig;
/**
* A BitmapDrawable that keeps track of whether it is being displayed or cached.
* When the drawable is no longer being displayed or cached,
* {#link Bitmap#recycle() recycle()} will be called on this drawable's bitmap.
*/
public class RecyclingBitmapDrawable extends BitmapDrawable {
private int cacheRefCount = 0;
private int displayRefCount = 0;
private boolean hasBeenDisplayed;
public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
/**
* Notify the drawable that the displayed state has changed. Internally a
* count is kept so that the drawable knows when it is no longer being
* displayed.
*
* #param isDisplayed
* - Whether the drawable is being displayed or not
*/
public void setIsDisplayed(boolean isDisplayed) {
synchronized (this) {
if (isDisplayed) {
displayRefCount++;
hasBeenDisplayed = true;
} else {
displayRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
}
/**
* Notify the drawable that the cache state has changed. Internally a count
* is kept so that the drawable knows when it is no longer being cached.
*
* #param isCached
* - Whether the drawable is being cached or not
*/
public void setIsCached(boolean isCached) {
synchronized (this) {
if (isCached) {
cacheRefCount++;
} else {
cacheRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
}
private synchronized void checkState() {
// If the drawable cache and display ref counts = 0, and this drawable
// has been displayed, then recycle
if (cacheRefCount <= 0 && displayRefCount <= 0 && hasBeenDisplayed && hasValidBitmap()) {
if (BuildConfig.DEBUG)
Log.d(MyApp.TAG, "No longer being used or cached so recycling. " + toString());
getBitmap().recycle();
}
}
private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
}
}
This is one more class.
package com.mycompanyname.myapp.ui.cache;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* Sub-class of ImageView which automatically notifies the drawable when it is
* being displayed.
*/
public class RecyclingImageView extends ImageView {
public RecyclingImageView(Context context) {
super(context);
}
public RecyclingImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* #see android.widget.ImageView#onDetachedFromWindow()
*/
#Override
protected void onDetachedFromWindow() {
// This has been detached from Window, so clear the drawable
setImageDrawable(null);
super.onDetachedFromWindow();
}
/**
* #see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)
*/
#Override
public void setImageDrawable(Drawable drawable) {
// Keep hold of previous Drawable
final Drawable previousDrawable = getDrawable();
// Call super to set new Drawable
super.setImageDrawable(drawable);
// Notify new Drawable that it is being displayed
notifyDrawable(drawable, true);
// Notify old Drawable so it is no longer being displayed
notifyDrawable(previousDrawable, false);
}
#Override
public void setImageResource(int resId) {
// Keep hold of previous Drawable
final Drawable previousDrawable = getDrawable();
super.setImageResource(resId);
// Notify new Drawable that it is being displayed
final Drawable newDrawable = getDrawable();
notifyDrawable(newDrawable, true);
// Notify old Drawable so it is no longer being displayed
notifyDrawable(previousDrawable, false);
}
/**
* Notifies the drawable that it's displayed state has changed.
*
* #param drawable
* #param isDisplayed
*/
private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
if (drawable != null) {
if (drawable instanceof RecyclingBitmapDrawable) {
// The drawable is a CountingBitmapDrawable, so notify it
((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
} else if (drawable instanceof LayerDrawable) {
// The drawable is a LayerDrawable, so recurse on each layer
LayerDrawable layerDrawable = (LayerDrawable) drawable;
for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
}
}
}
}
}
Now in your resource file (activity or else). Let's say this is user profile avatar. I saw sometimes it may be couple MB or more )
<com.mycompanyname.myapp.ui.cache.RecyclingImageView
android:id="#+id/profile_icon"
android:layout_width="120dp"
android:layout_height="120dp"
android:adjustViewBounds="true"
android:contentDescription="#string/dummy_desc"
android:scaleType="centerCrop"
android:src="#drawable/icon_avatar_placeholder" />
In case you need to load lots of images in your app (let's say, it is list of user profiles with their avatars) then you also need some kind of image cache. Not sure you need it right now. In case you do need it, just ping me here on stackoverflow and I will add more.
Hope this helps.
I'm new to Android and trying to create a simple game called SoS. It's close to complete however I can not figure out how to scale the images to put in the imagebuttons. The game board is created during runtime, the user chooses a board size, 6 x 6, 7 x 7, or 8 x 8 (ie: a board of 36, 49 or 64 tiles(imagebuttons)). Depending on the screen size/boardsize the images may or may not fit in the imagebuttons. So I decided to try and scale the images to the size of the ImageButton. I tried to scale the resource image to the correct size in the constructor of the Tile(imagebutton), and then store the result in a instance variable, however I get a runtime exception, and i'm not sure how to fix it. In the scaleImages method if I change the this.getLength(), and this.getWidth() to 50 it runs with out error but no images are set in the ImageButton. I think I need a way to determine the size of the button before I try and scale the image.
public class Tile extends ImageButton {
private boolean occupied;
private boolean isS;
private boolean isO;
int countClicks = 0;
private static Drawable sImageScaled;
private static Drawable oImageScaled;
private static Drawable emptyImageScaled;
public Tile(Context context) {
super(context);
scaleImages(context);
}
public Tile(Context context, AttributeSet attrs) {
super(context, attrs);
scaleImages(context);
}
public Tile(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
scaleImages(context);
}
public void setDefaults() {
occupied = false;
isS = false;
isO = false;
this.setBackground(emptyImageScaled);
}
// Method will set the background image of the Tile
// instance variable will keep track of which image is displayed and
// will change with every click
public void setTile() {
++countClicks;
switch (countClicks) {
case 1:
this.setBackground(sImageScaled);
isS = true;
isO = false;
break;
case 2:
this.setBackground(oImageScaled);
isO = true;
isS = false;
break;
case 3:
this.setBackground(emptyImageScaled);
isO = false;
isS = false;
}
// reset to 0
if (countClicks == 3) {
countClicks = countClicks % 3;
}
}
public void setOccupied(boolean val) {
occupied = val;
}
private void scaleImages(Context context){
// scale the s image
Drawable sDr = getResources().getDrawable(R.drawable.ic_simageorange);
Bitmap sBitmap = Bitmap.createBitmap(sDr.getIntrinsicWidth(), sDr.getIntrinsicHeight(), Config.ARGB_8888);
// Scale it
sImageScaled = new BitmapDrawable(context.getResources(), Bitmap.createScaledBitmap(sBitmap, this.getWidth(), this.getHeight(), true));
// scale the o image
Drawable oDr = getResources().getDrawable(R.drawable.ic_oimage);
Bitmap oBitmap = Bitmap.createBitmap(oDr.getIntrinsicWidth(), oDr.getIntrinsicHeight(), Config.ARGB_8888);
// Scale it
sImageScaled = new BitmapDrawable(context.getResources(), Bitmap.createScaledBitmap(oBitmap, this.getWidth(), this.getHeight(), true));
// scale the empty image
Drawable eDr = getResources().getDrawable(R.drawable.ic_tile);
Bitmap eBitmap = Bitmap.createBitmap(eDr.getIntrinsicWidth(), eDr.getIntrinsicHeight(), Config.ARGB_8888);
// Scale it to
emptyImageScaled = new BitmapDrawable(context.getResources(), Bitmap.createScaledBitmap(eBitmap, this.getWidth(), this.getHeight(), true));
}
}
Here's the code in the Activity that creates the views and the Tiles(ImageButtons)
// Method adds the views to the tableview
public void showGameBoard() {
int tilePadding = 1;
//galaxy S4
int tileWH = 100;
// Layout parameters for the TableRows
// (Layout params are always the parent class of the object receiving
// the param)
TableLayout.LayoutParams rowLp = new TableLayout.LayoutParams(
(tileWH * tilePadding) * boardSize.getWidth(), tileWH
* tilePadding, 1.0f);
// Layout parameters for the TableCells
TableRow.LayoutParams cellLp = new TableRow.LayoutParams(tileWH
* tilePadding, tileWH * tilePadding, 1.0f);
// for every row
for (int row = 0; row < tiles.length; row++) {
// create a new table row
TableRow tableRow = new TableRow(this);
// set the height and width of the row
tableRow.setLayoutParams(rowLp);
// for every column
for (int col = 0; col < tiles[row].length; col++) {
// add some padding to the tiles
tiles[row][col].setPadding(tilePadding, tilePadding,
tilePadding, tilePadding);
// add the tile to the table row
tableRow.addView(tiles[row][col], cellLp);
}
// add the row to the board layout
game_board.addView(tableRow);
}
}
// Method will set up the Tiles and the listeners
public void createGameBoard() {
// set total rows and columns based on the size of the board
int totalRows = boardSize.getWidth();
int totalCols = boardSize.getLength();
// setup the tiles array
tiles = new Tile[totalRows][totalCols];
// for every row
for (int row = 0; row < tiles.length; row++) {
// for every column
for (int col = 0; col < tiles[row].length; col++) {
// create a tile (ImageButton)
tiles[row][col] = new Tile(this);
// set the tile (ImageButton) defaults
tiles[row][col].setDefaults();
final int curRow = row;
final int curCol = col;
Well if you can solve my issue or point me in the right direction it'd be appreciated.
I Don't know how your images for ImageButton looks like, maybe 9-patch will help you.
Here is simple guide about 9-patch. To make 9-patch image simply, change your image's file name from foo.png into foo.9.png, open it with android studio and draw fill area and scale area appropriately. if you don't use android studio, use sdk tools in /your_sdk_dir/toos/draw9patch. because this is very simple tool, it needs no explanation.
I am using NetworkImageView to show some covers downloaded from a remote URL and I successfully manage to cache and show them, but I want to let users set their own cover images if they want.
I tried to use setImageUrl method with Uri.fromFile(mCoverFile).toString() as arguments, but it doesn't work. Since it is a mix of remote and local images I can't switch to regular ImageViews, so I was wondering if there's any way to enable loading of local images.
I am of course aware of the ImageView's setImageBitmap method, but NetworkImageView automatically resizes the created Bitmap and also prevents View recycling in GridViews and ListViews.
UPDATE: njzk2's answer did the trick. To autoresize the Bitmap according to your View size, then just copy the ImageRequest.doParse method from Volley's source.
NetworkImageView uses ImageLoader, which in turn uses an ImageCache.
You can provide a custom ImageCache with your images, provided you use the same mechanism for keys:
return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
.append("#H").append(maxHeight).append(url).toString();
url is not tested before the actual request would be done, so no issue here.
Typically, your 'cache' could look like :
public class MyCache implements ImageLoader.ImageCache {
#Override
public Bitmap getBitmap(String key) {
if (key.contains("file://")) {
return BitmapFactory.decodeFile(key.substring(key.indexOf("file://") + 7));
} else {
// Here you can add an actual cache
return null;
}
}
#Override
public void putBitmap(String key, Bitmap bitmap) {
// Here you can add an actual cache
}
}
You use it like :
imageView.setImageUrl(Uri.fromFile(mCoverFile).toString(), new MyCache());
(This has not been actually tested and there may be some adjustments to do)
Thank you for your answer. I wrote some code based on your help.
usage: just use LocalImageCache.class as Cache. No more code to change.
private ImageLoader mLocalImageLoader;
mLocalImageLoader = new ImageLoader(mRequestQueue,
new LocalImageCache(mCtx));
NetworkImageView test = (NetworkImageView) findViewById(R.id.iv_test);
test.setImageUrl("/storage/emulated/0/DCIM/Philm/2017_03_24_01_.png", MySingleton.getInstance(this.getApplicationContext()).getLocalImageLoader());
public class LocalImageCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {
public LocalImageCache(int maxSize) {
super(maxSize);
}
public LocalImageCache(Context ctx) {
this(getCacheSize(ctx));
}
#Override
public Bitmap getBitmap(String key) {
key = key.substring(key.indexOf("/"));
Bitmap result = get(key);
Log.d("TAG", key);
if (result == null) {
Bitmap temp = BitmapFactory.decodeFile(key);
put(key, temp);
return temp;
} else {
return result;
}
}
#Override
public void putBitmap(String key, Bitmap bitmap) {
// Here you can add an actual cache
// Never touch here
}
// 默认屏幕5倍的图片缓存
// Returns a cache size equal to approximately three screens worth of images.
public static int getCacheSize(Context ctx) {
final DisplayMetrics displayMetrics = ctx.getResources().
getDisplayMetrics();
final int screenWidth = displayMetrics.widthPixels;
final int screenHeight = displayMetrics.heightPixels;
// 4 bytes per pixel
final int screenBytes = screenWidth * screenHeight * 4;
return screenBytes * 5;
}
#Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
}
NetworkImageView extends ImageView. You should be able to use the same methods as a regular ImageView
image.setImageResource(R.drawable.my_image);
or
imageView.setImageBitmap(BitmapFactory.decodeFile(imagePath));
I need to know which image is in which cell, so I have only tried using .setId() and .getId(). But if there's any other way to do this, just let me know.
Images are added when I click a button. I have nothing showing where they appear, but here's my problem, I'm trying to add a Id to each image that I'm able to add. So when I drop them in the GridView and click one of them I can do something else if the Id is the correct one, like, if (Id == 1) open... else if (id == 2) open other thing. But I can't get the Id to be saved to the image (or saved to that ImageCell).
Example of the problem I'm getting: If I added image1 which have Id1 and then add image2 which have Id2 the image1 Id will change to the last image added (in this case image2 Id). I have tried a lot of different ways of setting the Id, but I'm still getting the last image id in the first one dropped.
I'm using a Toast to show the image dropped Id. This is the ImageCell code.
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo)
{
mEmpty = false;
ImageView sourceView = (ImageView) source;
sourceView.setId(DragActivity.m);
Drawable d = sourceView.getDrawable ();
this.setId(sourceView.getId());
if (d != null) {
this.setImageDrawable (d);
this.setId(sourceView.getId());
prueba = this.getId();
}
toast ("" + prueba);
}
DragActivity.m is just a variable that's used to change the picture.
public void addNewImageToScreen() {
int resourceId = R.drawable.hello;
m = mImageCount % 3;
if (m == 1) {
resourceId = R.drawable.photo1;
} else if (m == 2) {
resourceId = R.drawable.photo2;
}
addNewImageToScreen(resourceId);
}
Anyone know what I'm doing wrong? Thanks
I think you are trying to swap images in that case you can identify ImageView uniquely by tag like this:
ImageView imageView = new ImageView(_context);
imageView.setTag(1);
And then on GridView's / ImageView's click check its tag like this:
gridview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Tag = (Integer) arg1.getTag();
}
}