I am pretty new new in Android and I am finding some dificulties to do the following thing related to the Context.
So I have an utility class that contain an utility method that create and return a Bitmap image, this is the code of my class:
public class ImgUtility {
/**
* Method that create the images related to the difficulty of a recepy
* #param context
* #param difficulty that represent the number of chef_hat_ok into the final image
* #return a Bitmap representing the difficult of a recepy
*/
public static Bitmap createRankingImg(Context context, int difficulty) {
// Create a Bitmap image starting from the star.png into the "/res/drawable/" directory:
Bitmap myBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.chef_hat_ok);
// Create a new image bitmap having width to hold 5 star.png image:
Bitmap tempBitmap = Bitmap.createBitmap(myBitmap.getWidth() * 5, myBitmap.getHeight(), Bitmap.Config.RGB_565);
/* Attach a brand new canvas to this new Bitmap.
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components:
1) a Bitmap to hold the pixels.
2) a Canvas to host the draw calls (writing into the bitmap).
3) a drawing primitive (e.g. Rect, Path, text, Bitmap).
4) a paint (to describe the colors and styles for the drawing).
*/
Canvas tempCanvas = new Canvas(tempBitmap);
// Draw the image bitmap into the cavas:
tempCanvas.drawBitmap(myBitmap, 0, 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth(), 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth() * 2, 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth() * 3, 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth() * 4, 0, null);
return tempBitmap;
}
}
As you can see this class contains the createRankingImg() that take the Context object as parameter and use it to create an image. This object is used to retrieve an image from the resources (into the BitmapFactory.decodeResource() method). What exactly represent the Context object into an Android application?
I know that to obtain the context into an activity class I can use the getResources() method.
My problem is that I have to obtain the context into a class that exetends Fragment.
I have something like this:
public class ScreenSlidePageFragment extends Fragment {
.......................................................................
.......................................................................
.......................................................................
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
.....................................................................
.....................................................................
.....................................................................
switch (mPageNumber + 1) {
case 1:
imgSlideView.setImageResource(R.drawable.carbonara);
((TextView) rootView.findViewById(android.R.id.text1)).setText(getString(R.string.carbonara));
ImageView difficultyContainerImageView1 = (ImageView) rootView.findViewById(R.id.difficultyContainer);
difficultyContainerImageView1.setImageDrawable(new BitmapDrawable(getResources(), ImgUtility.createRankingImg(getApplicationContext(), 3)));
break;
.....................................................................
.....................................................................
.....................................................................
}
return rootView;
}
My problem is that when in the previous fragment class I call the method:
ImgUtility.createRankingImg(getResources(), 3)
passing to it the getResources() output (that I thinked give me the Context, the IDE give me the following error message:
Wrong 1st argument type. Found: 'android.content.res.Resources',
required: 'android.content.Context'
So it seems to me that into a class that extends a Fragment and not an Activity the getResources() method return a Resources object instead a Context object (as done into an Activity class). Is it true? Why?
How can I obtain the Context inside a class that extends Fragment? And what exactly represent the Context in an Android app? What am I missing? How can I sole this issue?
Create an initial function that takes Context as a parameter so you can pass the context object from the activity class.
Related
When adding markers in mapbox, there is a provision to add custom icon to the marker. I wonder whether we can inflate a view(R.layout file) instead of assigning a drawable icon.
Here is the code:-
public void onMapReady(MapboxMap mapboxMap) {
IconFactory iconFactory=IconFactory.getInstance(context);
for(int i=0;i<coordinates.size();i++){
mapboxMap.addMarker(new MarkerOptions()
.position(new LatLng(lat,longt))
.icon(iconFactory.fromResource(R.drawable.ic_location_green))
//can we inflate a view here instead of assigning a drawable image?
}
}
I don't think that it is possible
What you can do is draw custom icon at runtime:
Draw it on Canvas
Generate Drawable at runtime (instead of creating xml, you can create an object. So if you were to type <shape>, you can replace it with new Shape(); in Java)
Generate a view and copy its bitmap (How to convert Views to bitmaps?) this option would provive only looks of it - things like click listeners will not work, thus I don't see a reason for choosing this option
This possible using the following utility class:
/**
* Utility class to generate Bitmaps for Symbol.
* <p>
* Bitmaps can be added to the map with {#link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)}
* </p>
*/
private static class SymbolGenerator {
/**
* Generate a Bitmap from an Android SDK View.
*
* #param view the View to be drawn to a Bitmap
* #return the generated bitmap
*/
public static Bitmap generate(#NonNull View view) {
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measureSpec, measureSpec);
int measuredWidth = view.getMeasuredWidth();
int measuredHeight = view.getMeasuredHeight();
view.layout(0, 0, measuredWidth, measuredHeight);
Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
bitmap.eraseColor(Color.TRANSPARENT);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
}
A full example integrating this code can be found here. Note that this code can be executed on a background thread, so you don't need to block the main thread for it. While we don't expose a binary atm we are looking into creating a little plugin around this code. The feature request for it can be found here.
I am absolutly new in Android development and I am developing my first education app related to a beginner course on Udacity.
The application idea is pretty simple: it is a cousine recipes application in which the use swap from a recepy to another swapping slides right or left in the main activity.
So this is my GitHub repository related to this application: https://github.com/AndreaNobili/PastaFromRome
To swap from a recepy to another I used the fragment, so basically I have an activity_man.xml file that only contains the header and a viewpager for the fragment:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Then I have the fragment_screen_slide_page.xml that represent a slide related to a single recipe, something like this:
<!-- Dummy content. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="0dp">
<ImageView
android:id="#+id/imageView1"
android:layout_width="fill_parent"
android:scaleType="fitXY"
android:layout_height="250dp" />
<!--
android:src="#drawable/carbonara"/>
android:background="#drawable/carbonara" />
-->
<TextView android:id="#android:id/text1"
style="#style/pastaTitleTextStyle" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0px"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
style="#style/HeaderTextStyle"
android:text="Difficoltà:" />
<ImageView
android:id="#+id/difficultyContainer"
android:layout_width="fill_parent"
android:scaleType="fitXY"
android:layout_height="55dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
This view is a fragmetn that, when is showed, contain the current recipe.
It contains a first ImageView having id imageView1 that show an image related to the current recipe, it works fine.
Then it contains a second ImageView having id difficultyContainer that is an difficulty ranking image created at runtime from the application. Here is where I am going crazy !!!
This is the ScreenSlidePageFragment class (that extends Fragment) that handle the logic of the fragment:
/**
* A fragment representing a single step in a wizard. The fragment shows a dummy title indicating
* the page number, along with some dummy text.
*
*/
public class ScreenSlidePageFragment extends Fragment {
private static final String TAG = "ScreenSlidePageFragment";
/**
* The argument key for the page number this fragment represents.
*/
public static final String ARG_PAGE = "page";
/**
* The fragment's page number, which is set to the argument value for {#link #ARG_PAGE}.
*/
private int mPageNumber;
private static Context context;
/**
* Factory method for this fragment class. Constructs a new fragment for the given page number.
*/
public static ScreenSlidePageFragment create(int pageNumber, Context baseContext) {
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
Bundle args = new Bundle();
args.putInt(ARG_PAGE, pageNumber);
fragment.setArguments(args);
context = baseContext;
return fragment;
}
public ScreenSlidePageFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPageNumber = getArguments().getInt(ARG_PAGE);
}
/**
* Method that will be called when the "pasta slider" is slided right or left
* #param inflater
* #param container
* #param savedInstanceState
* #return
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println("PAGE NUMBER: "+ mPageNumber);
// Obtain the colosseum_icon.png from the rsources as a Drawable:
//Drawable drawable = getResources().getDrawable(R.drawable.colosseum_icon);
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
ImageView imgSlideView = (ImageView) rootView.findViewById(R.id.imageView1);
/* Retrieve the ImageView having id="difficultyContainer" (where to put the diifficulty
image created):
*/
//ImageView difficultyContainerImageView = (ImageView) rootView.findViewById(R.id.difficultyContainer);
// Load the 2 images for the creation of the "difficulty graphic":
Bitmap chefHatWhite = BitmapFactory.decodeResource(getResources(), R.drawable.chef_hat_white);
Bitmap chefHatOk = BitmapFactory.decodeResource(getResources(), R.drawable.chef_hat_ok);
// Where the previus image will be drawn:
//Canvas canvas = new Canvas();
Canvas difficultyCanvas = creaImgDifficulty();
//switch (mPageNumber + 1) {
switch (mPageNumber) {
case 0:
imgSlideView.setImageResource(R.drawable.carbonara);
((TextView) rootView.findViewById(android.R.id.text1)).setText(getString(R.string.carbonara));
ImageView difficultyContainerImageView1 = (ImageView) rootView.findViewById(R.id.difficultyContainer);
difficultyContainerImageView1.setImageDrawable(new BitmapDrawable(getResources(), ImgUtility.createRankingImg(context, 3)));
break;
case 1:
imgSlideView.setImageResource(R.drawable.amatriciana);
((TextView) rootView.findViewById(android.R.id.text1)).setText(getString(R.string.amatriciana));
break;
case 2:
imgSlideView.setImageResource(R.drawable.gricia_m);
((TextView) rootView.findViewById(android.R.id.text1)).setText(getString(R.string.gricia));
break;
case 3:
imgSlideView.setImageResource(R.drawable.cacio_e_pepe);
((TextView) rootView.findViewById(android.R.id.text1)).setText(getString(R.string.cacio_e_pepe));
break;
case 4:
imgSlideView.setImageResource(R.drawable.ajo_ojo_e_peperoncino);
((TextView) rootView.findViewById(android.R.id.text1)).setText(getString(R.string.ajo_ojo_peperoncino));
break;
}
return rootView;
}
private void createImg(ImageView imgview) {
Canvas canvas;
Bitmap star = BitmapFactory.decodeResource(getResources(), R.drawable.star);
Bitmap output = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
canvas = new Canvas(output);
canvas.drawBitmap(star, star.getWidth() + 2, 0, null);
imgview.setImageDrawable(new BitmapDrawable(getResources(), output));
}
private Canvas creaImgDifficulty() {
Canvas canvas;
Bitmap chefHatOk = BitmapFactory.decodeResource(getResources(), R.drawable.chef_hat_ok);
Bitmap output = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
canvas = new Canvas(output);
int space = 10; // the space between images
for(int i = 0; i < 3; i++) {
canvas.drawBitmap(chefHatOk, i * (chefHatOk.getWidth() + space), 0, null);
}
return canvas;
}
/**
* Returns the page number represented by this fragment object.
*/
public int getPageNumber() {
return mPageNumber;
}
}
So basically into the onCreateView() method (that I think is performed when the current fragment is loaded) I have the mPageNumber variable that specify the loaded fragment related to a specific recipe and there is a switch case used to load the specific information of this recipe into this fragment, for example:
case 0: it is the first recipe named carbonara so it put the carbonara.png image (R.drawable.carbonara) into the previous ImageView havving id=imageView1 defined into the fragment_screen_slide_page.xml file.
It works fine.
The problem occur when in the previous code I do:
ImageView difficultyContainerImageView1 = (ImageView) rootView.findViewById(R.id.difficultyContainer);
difficultyContainerImageView1.setImageDrawable(new BitmapDrawable(getResources(), ImgUtility.createRankingImg(context, 3)));
As you can see in the code, the difficultyContainerImageView1 identify the ImageView having id=difficultyContainer in my fragment. Here I set an Image created by the ImgUtility.createRankingImg() method.
public class ImgUtility {
/**
* Method that create the images related to the difficulty of a recepy
* #param context
* #param difficulty that represent the number of chef_hat_ok into the final image
* #return a Bitmap representing the difficult of a recepy
*/
public static Bitmap createRankingImg(Context context, int difficulty) {
// Create a Bitmap image starting from the star.png into the "/res/drawable/" directory:
Bitmap myBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.chef_hat_ok);
// Create a new image bitmap having width to hold 5 star.png image:
Bitmap tempBitmap = Bitmap.createBitmap(myBitmap.getWidth() * 5, myBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas tempCanvas = new Canvas(tempBitmap);
// Draw the image bitmap into the cavas:
tempCanvas.drawBitmap(myBitmap, 0, 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth(), 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth() * 2, 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth() * 3, 0, null);
tempCanvas.drawBitmap(myBitmap, myBitmap.getWidth() * 4, 0, null);
return tempBitmap;
}
}
This method should be ok because it seems to me that return a Bitmap but this image is not shown into the fragment ImageView having id=difficultyContainer
Into the logcat I can see the following error log but I don't know if are related to my problem or not:
08-21 19:38:12.315 4239-4925/com.example.android.pastafromrome W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (8730x1836, max=4096x4096)
08-21 19:38:12.335 4239-4925/com.example.android.pastafromrome W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (8730x1836, max=4096x4096)
08-21 19:38:12.335 4239-4925/com.example.android.pastafromrome W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (8730x1836, max=4096x4096)
Why? What I missing? How can I fix this issue? I will very glad to you if you see my GitHub repository and help me to fix it.
The problem is that Android will automaticly resize chef_hat_ok to 1746px width (I guess it depends on device). It seems alright, but you do 5 of them and 5 x 1746px is 8370 and texture can't hold that.
Solution: disable scaling or put resources that depends on density (didn't checked that option).
Disabling scaling.
ImgUtility.java
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap myBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.chef_hat_ok, options);
Edit: you will need to set density to canvas tempCanvas.setDensity(myBitmap.getDensity());
Edit2: I would also suggest to make some changes to difficulty container
<ImageView
android:id="#+id/difficultyContainer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scaleType="centerInside"
android:adjustViewBounds="true" />
I have 92 images and I would like to have an indicator such as a check mark to indicate that an image is unlocked. I have the check mark in a .png file and what I tried at first was to just make a seperate copy of each image with the check mark put on top of the image in photoshop. However I know that there must be a simpler way of just adding the check mark file on top of the image that is already there instead of having a copy of the image with the check mark already on it.
I have a GridViewAdapter class responsible for loading the original images into a gridview:
#Override public View getView(int position, View convertView, ViewGroup parent) {
SquaredImageView view = (SquaredImageView) convertView;
if (view == null) {
view = new SquaredImageView(context);
view.setScaleType(CENTER_CROP);
}
// Get the image URL for the current position.
Integer url = getItem(position);
// Trigger the download of the URL asynchronously into the image view.
Picasso.with(context) //
.load(url) //
//
.error(R.drawable.error) //
.fit() //
.into(view);
return view;
}
where url is a list that contains the references to each image to be loaded
the mentioned SquaredImageView class is:
/** An image view which always remains square with respect to its width. */
final class SquaredImageView extends ImageView {
public SquaredImageView(Context context) {
super(context);
}
public SquaredImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}
}
Any suggestions are appreciated
I can think of a few ways to do this:
Option 1:
Wrap the SquaredImageView in a Framelayout and place another SqauredImageView inside of it. Set the check mark on the second image view.
Option 2:
Wrap the SquaredImageView in a FrameLayout and set the checkmark image on the foreground property of the FrameLayout (using setForeground).
Option 3:
Create a ForegroundSquaredImageView which supports overlaying an image (similar to the foreground property of FrameLayout).
The code for something like this can be found here: https://gist.github.com/JakeWharton/0a251d67649305d84e8a. You'll need to change it to extend from your SquaredImageView.
I want to store images in my database. Also I want to check that if the image and title is already in the database. If so, it will not add them to the database. This is my class.
Attractions
public class Attractions extends ListActivity {
DataBaseHandler db = new DataBaseHandler(this);
ArrayList<Contact> imageArry = new ArrayList<Contact>();
List<Contact> contacts;
ContactImageAdapter adapter;
int ctr, loaded;
int [] landmarkImages={R.drawable.oblation,R.drawable.eastwood,R.drawable.ecopark,R.drawable.circle};
String []landmarkDetails = { "Oblation", "Eastwood", "Ecopark", "QC Circle"};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_attractions);
ctr = db.checkContact(landmarkDetails[loaded]);
// get image from drawable
/**
* CRUD Operations
* */
// Inserting Contacts
Log.d("Insert: ", "Inserting ..");
for(loaded=0; loaded <landmarkDetails.length;loaded++){
Bitmap image = BitmapFactory.decodeResource(getResources(),
landmarkImages[loaded]);
// convert bitmap to byte
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte imageInByte[] = stream.toByteArray();
Log.d("Going to load images", "Image "+ loaded);
Log.d("Goind to load objects", "loading");
if(ctr == 0){
Log.d("Nothing Loaded", "Loading Now");
db.addContact(new Contact(landmarkDetails[loaded], imageInByte));}
Log.d(landmarkDetails[loaded], "Loaded!");
image.recycle();
}
loadFromDb();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.attractions, menu);
return true;
}
public void loadFromDb(){
// Reading all contacts from database
contacts = db.getAllContacts();
for (Contact cn : contacts) {
String log = "ID:" + cn.getID() + " Name: " + cn.getName()
+ " ,Image: " + cn.getImage();
// Writing Contacts to log
Log.d("Result: ", log);
//add contacts data in arrayList
imageArry.add(cn);
}
adapter = new ContactImageAdapter(this, R.layout.screen_list,
imageArry);
ListView dataList = (ListView) findViewById(android.R.id.list);
dataList.setAdapter(adapter);
}
public void onPause(){
super.onPause();
}
public void onResume(){
super.onResume();
}
}
It works fine on the emulator, but I tried testing on my S4 and then after 3 tries of going to this class, it forced stop. I tried it with usb debugging and the logcat showed java.lang.outofmemoryerror . The logcat pointed the error in my contactimageadapter.
ContactImageAdapter
public class ContactImageAdapter extends ArrayAdapter<Contact>{
Context context;
int layoutResourceId;
// BcardImage data[] = null;
ArrayList<Contact> data=new ArrayList<Contact>();
public ContactImageAdapter(Context context, int layoutResourceId, ArrayList<Contact> data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ImageHolder holder = null;
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ImageHolder();
holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle);
holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
row.setTag(holder);
}
else
{
holder = (ImageHolder)row.getTag();
}
Contact picture = data.get(position);
holder.txtTitle.setText(picture._name);
//convert byte to bitmap take from contact class
byte[] outImage=picture._image;
ByteArrayInputStream imageStream = new ByteArrayInputStream(outImage);
Bitmap theImage = BitmapFactory.decodeStream(imageStream);
holder.imgIcon.setImageBitmap(theImage);
return row;
}
static class ImageHolder
{
ImageView imgIcon;
TextView txtTitle;
}
}
And pointed to this line Bitmap theImage = BitmapFactory.decodeStream(imageStream);
I have little (almost none) knowledge on managing images and storing them. I also enable android:largeHeap but still force closes on multiple tries. I hope someone can help me solving this issue, or at least show me a different way of storing text and images to sqlite db. Many thanks!
You have multiple places where whole image (assuming it is big) keeps in memory:
Contact object has it. All loaded images are in imageArry which is instance level variable.
public class Attractions extends ListActivity {
DataBaseHandler db = new DataBaseHandler(this);
ArrayList<Contact> imageArry = new ArrayList<Contact>();
in ContactImageAdapter.getView method you create another copy of image as BMP in holder object and pass it out of method.
So, at some point you do not have enough memory to keep all of them. Also I sure that decodeStream needs some more memory to perform.
After all it is not predictable when each new holder created in getView will be cleaned by GC.
Usually for such situation when object created as new in some method, then passed back to the calling method, that object will be collected only by Full GC.
So, as "Software Sainath" said, do not store images in database…
and do not keep them in memory either.
P.S. Then provide to the view a link to the external image file. That also will save time to load a view. Image will be in cache and if user at least once got it, it will not pass through the network again.
I guess images there are not frequently change them self. another image of Contact will be another file…
I wrote an answer to the somewhat similar problem some while ago, here is the link that you can check. The problem is in the approach of saving the images into the database, you should not be doing this. Instead, write the images as files on the phone memory and use it further.
Don't store Image to Sqlite Database eventually, you will ran into out of memory error after three or five image saved to database. It's not the best practice, maximum memory allocated for field in a row in sqlite is less than 3mb, be aware of this.
Instead of saving Images to database, Keep the images inside your app folder, save the path to the Database.
Your are loading your image as it is to your Image adapter. Let's say your image is 1280x720 resolution and 2mb in size, it will take the same space in your memory Heap.
You can either scaledown your image and load it as bitmap to your Adapter ImageView like this.
Before loading your image as Bitmap get it height and width.
//Code read the image and give you image height and width.it won't load your bitmap.
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true;
BitmapFactory.decodeFile(your_image_url,option);
int image_original_width = option.outHeight;
int image_original_height = option.outWidth;
Now to scale down your Image you have to know the ImageView width and height. This is because we are going to scale down the image matching the imageview with pixel perfection.
int image_view_width = image_view.getWidht();
int image_view_height = image_view.getHeight();
int new_width;
int new_height;
float scaled_width;
if(image_original_width>image_view_width)
{ //if the image_view width is lesser than original_image_width ,you have to scaled down the image.
scale_value =(float)image_original_width/(float)image_view_width;
new_width = image_original_width/scaled_value;
new_height = image_orignal_height/scale_value
}
else
{
// use the image_view width and height as sacling value widht and height;
new_width = image_view_width;
new_height = image_view_height;
}
Now Scale Down your bitmap and load it like this.
// this will load a bitmap with 1/4 the size of the original one.
// this to lower your bitmap memory occupation in heap.
BitmapFactory.Options option = new BitmapFactory.Options();
option.inSampleSize = 4;
Bitmap current_bitmap = BitmapFactory.decodeFile(image_url,option);
Bitmap scaled_bitmap = Bitmap.createScaledBitmap(current_bitmap,new_width,new_height,true);
holder.imgIcon.setImageBitmap(scaled_bitmap);
//release memory occupied by current_bitmap in heap, as we are no longer using it.
current_bitmap.recycle();
If you want to understand a little more about Bitmap and memory view this link.
If you don't want to handle rescaling bitmap by yourself. you can use Glide or Picasso library which does the same.
I have written an article about using Picasso to load image in listview, which will help you to start, if you are looking to use picasso.
http://codex2android.blogspot.in/2015/11/picasso-android-example.html
Please make sure to use the quick garbage collection eligible reference type while loading the images from the network
import java.lang.ref.SoftReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import android.graphics.Bitmap;
public class MemoryCache {
private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());
public Bitmap get(String id){
if(!cache.containsKey(id))
return null;
SoftReference<Bitmap> ref=cache.get(id);
return ref.get();
}
public void put(String id, Bitmap bitmap){
cache.put(id, new SoftReference<Bitmap>(bitmap));
}
public void clear() {
cache.clear();
}
}
Don't store Image to Sqlite Database. It's not the best practice.
Instead of saving Images to database, Keep the images in a storage, but if you want to keep them private then keep them inside your app folder and save the path to the Database.
Use one of the well known libraries like http://square.github.io/picasso/ or https://github.com/bumptech/glide, they offer great help with memory issues and also some cool transition effects.
I recommend using Glide because it works very well on device with low memory restrictions
I use the following code to initialize my bitmap variable:
Bitmap bitmap = ((BitmapDrawable)this.model.gameView.context.getResources().getDrawable(R.drawable.pic1)).getBitmap();
When I try to log the width of that bitmap, the log does not even output anything for that call.
I know it's making it to that line, because I traced the code.
Also, when I try to do canvas.draw for the bitmap, nothing is drawn on the screen.
Everything I draw with rectangles works fine.
My picture resource is a PNG.
Any help is appreciated!
Try something like this for your bitmap class.
public class DrawBitmap extends View
{
Bitmap bitmap;
public DrawBitmap(Context content)
{
super(content);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pic1);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);//whatever color you want, make sure it's not the same as your image
canvas.drawBitmap(bitmap, (canvas.getWidth()), 0, null);
}
}
Main Class
public class Main extends Activity
{
DrawBitmap myView;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myView = new DrawBitmap(this);
setContentView(myView);
}
}
Try using BitmapFactory.decodeResource
Have a look at the answer in this topic:
How to convert a Drawable to a Bitmap?
Just figured it out. It had nothing to do with the method of bitmap loading I used.
It was a logical error on my part. My code accidentally reached a case where my bitmap became null, and it tried to draw the null resource on the canvas.