I have a gridview with some images inside. Sometime when I'm scrolling, GC_FOR_ALLOC or heap is increased and I don't know why. I used a method to load a minimized image.
Might I use Picasso or a ViewHolder ? RecyclerView ?
The memory issues happened once or twice when I tested my app on my personal device although everything still ran. But on an older smartphone ( less efficient) , the memory issues totally stopped the phone and other apps from running.
I see warnings on my "setTag" as though the problem is caused by a memory leak.
How can I repair the memory problems ?
MainActivity.java
public class MainActivity extends Activity {
GridView gridView;
MediaPlayer mPlayer;
#Override
public synchronized void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean internetAcces = checkDataConnection();
if(internetAcces == true){
setContentView(R.layout.activity_main);
AdView adView = (AdView) this.findViewById(R.id.adView);
AdRequest adRequest1 = new AdRequest.Builder().build();
adView.loadAd(adRequest1);
}
else{
setContentView(R.layout.activity_main2);
}
gridView = (GridView)findViewById(R.id.gridview);
gridView.setAdapter(new MyAdapter(this));
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public synchronized void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
try{
if(mPlayer != null && mPlayer.isPlaying())
{
mPlayer.stop();
mPlayer.release();
playSong(position);
}
else
{
playSong(position);
}
}catch(Exception ex){
//Toast.makeText(MainActivity.this, ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
#Override
protected void onPause(){
super.onPause();
}
#Override
protected void onStop() {
super.onStop();
System.exit(0);
}
#Override
protected void onDestroy(){
super.onDestroy();
System.exit(0);
}
Here's MyAdapter.java
public final class MyAdapter extends BaseAdapter {
private final List<Item> mItems = new ArrayList<Item>(30);
private final LayoutInflater mInflater;
public Context context;
public MyAdapter(Context context) {
mInflater = LayoutInflater.from(context);
this.context = context;
mItems.add(new Item("Hé pélo", R.drawable.hepelo));
mItems.add(new Item("Salut les losers", R.drawable.salutleslosers));
mItems.add(new Item("Arrête Abib", R.drawable.abib));
mItems.add(new Item("Goût bite", R.drawable.goutbite));
mItems.add(new Item("Makassy", R.drawable.makassy));
mItems.add(new Item("Joris Lecon", R.drawable.jorislecon));
mItems.add(new Item("Cache la drogue", R.drawable.cacheladrogue));
mItems.add(new Item("Un peu trop tactile", R.drawable.unpeutroptactile));
mItems.add(new Item("Qui c'est qu'a touché à Dorian ?", R.drawable.dorian));
mItems.add(new Item("T'es pas beau", R.drawable.tespasbeau));
mItems.add(new Item("Ta mère", R.drawable.tamere));
mItems.add(new Item("Je bute ce paquet de chips", R.drawable.jebutecepaquetdechips));
mItems.add(new Item("La maison du content", R.drawable.lamaisonducontent));
mItems.add(new Item("Mes péripétiiiiiies", R.drawable.mesperipeties));
mItems.add(new Item("La rapta babtou", R.drawable.laraptababtou));
mItems.add(new Item("Que vas-tu faire ?!", R.drawable.bangbang));
mItems.add(new Item("Oh les tétés !", R.drawable.lestetes));
mItems.add(new Item("Mollé mollé", R.drawable.laraptababtou));
mItems.add(new Item("Okéééé", R.drawable.ok));
mItems.add(new Item("Guillaume le thug", R.drawable.guillaumelethug));
mItems.add(new Item("C'est ma teub", R.drawable.cestmateub));
mItems.add(new Item("Je suis qu'un thug", R.drawable.jsuisquunthug));
}
#Override
public int getCount() {
return mItems.size();}
#Override
public Item getItem(int i) {
return mItems.get(i);
}
#Override
public long getItemId(int i) {
return mItems.get(i).drawableId;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
View v = view;
ImageView picture;
TextView name;
if (v == null) {
v = mInflater.inflate(R.layout.grid_item, viewGroup, false);
v.setTag(R.id.picture, v.findViewById(R.id.picture));
v.setTag(R.id.text, v.findViewById(R.id.text));
}
picture = (ImageView) v.getTag(R.id.picture);
name = (TextView) v.getTag(R.id.text);
Item item = getItem(i);
//picture.setImageResource(item.drawableId);
picture.setImageBitmap(decodeSampledBitmapFromResource(context.getResources(),item.drawableId, 150, 150));
name.setText(item.name);
return v;}
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 = 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;}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private static class Item {
public final String name;
public final int drawableId;
Item(String name, int drawableId) {
this.name = name;
this.drawableId = drawableId;
}
}
}
main_activity.xml :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<GridView
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_gravity="center"
android:verticalSpacing="0dp"
android:layout_weight="1"
android:horizontalSpacing="0dp"
android:stretchMode="columnWidth"
android:numColumns="2"
android:listSelector="#android:color/transparent"
android:scrollingCache="false"
android:animationCache="false"
/>
<com.google.android.gms.ads.AdView
android:id="#+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
ads:adSize="SMART_BANNER"
ads:adUnitId="ca-app-pub-2373549811754777/2717783647"
/>
grid_item.xml :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.slamingresources.laboiteamisterv.SquareImageView
android:id="#+id/picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
/>
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="4dp"
android:paddingBottom="5dp"
android:layout_gravity="bottom"
android:textColor="#android:color/white"
android:background="#55000002"
/>
Logcat :
11-20 03:21:45.878: I/dalvikvm-heap(16815): Clamp target GC heap from 128.451MB to 128.000MB
11-20 03:21:47.628: I/dalvikvm-heap(16815): Clamp target GC heap from 135.930MB to 128.000MB
11-20 03:21:47.688: D/dalvikvm(16815): GC_BEFORE_OOM freed 109065K, 85% free 20521K/131004K, paused 59ms, total 60ms
11-20 03:22:00.498: D/dalvikvm(16815): GC_FOR_ALLOC freed 3972K, 85% free 20494K/131004K, paused 24ms, total 24ms
I know this is old but for those that are still looking for same answers: you can use getcachedir() to store data temporarily in Android.
For more options see Android data storage.
Yes, you got this error! Why? AFAIK, before Android 3.0 (I dont remember exactly) bitmap is not auto recycle. You must recycle bitmap after not using it.
To resolve your problem, I suggest you cache your Bitmap. When cached hit, recycle unused one.
Edit: I think the easiest way to solve your problem is use another library to help loading image. You can look at Universal ImageLoader. There's a function which can help you load with target size:
// Load image, decode it to Bitmap and return Bitmap synchronously
ImageSize targetSize = new ImageSize(80, 50); // result Bitmap will be fit to this size
Bitmap bmp = imageLoader.loadImageSync(imageUri, targetSize, options);
Related
I Have successfully started reading images back into my android app into a gridview, I need help now. When a user clicks on an image I want it to appear larger from the thumbnail. Any help is much appreciated.
My code for the images to get read :
[code]
public class View_Pictures extends AppCompatActivity {
public class ImageAdapter extends BaseAdapter {
private Context mContext;
ArrayList<String> itemList = new ArrayList<String>();
public ImageAdapter(Context c) {
mContext = c;
}
void add(String path){
itemList.add(path);
}
#Override
public int getCount() {
return itemList.size();
}
#Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);
imageView.setImageBitmap(bm);
return imageView;
}
public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;
// 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;
bm = BitmapFactory.decodeFile(path, options);
return bm;
}
public 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) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
}
ImageAdapter myImageAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view__pictures);
GridView gridview = (GridView) findViewById(R.id.gridview);
myImageAdapter = new ImageAdapter(this);
gridview.setAdapter(myImageAdapter);
String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();
String targetPath = ExternalStorageDirectoryPath + "/Venns Road Accident";
Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);
File[] files = targetDirector.listFiles();
for (File file : files){
myImageAdapter.add(file.getAbsolutePath());
}
}
}
Gridview
<GridView
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#id/textView47"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"/>
you can use photoviwer library which is used to show full detail image on seperate activity .
// Any implementation of ImageView can be used!
mImageView = (ImageView) findViewById(R.id.iv_photo);
// Set the Drawable displayed
Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);
// or
// get your bitmap here
mImageView.setImageDrawable(bitmap);
// Attach a PhotoViewAttacher, which takes care of all of the zooming functionality.
// (not needed unless you are going to change the drawable later)
mAttacher = new PhotoViewAttacher(mImageView);
please refer below link
https://github.com/chrisbanes/PhotoView
10-16 21:49:03.318 29852-29852/com.klau.jobsinaustralia I/dalvikvm-heap: Grow heap (frag case) to 171.874MB for 14400016-byte allocation
10-16 21:49:03.578 29852-29852/com.klau.jobsinaustralia I/dalvikvm-heap: Grow heap (frag case) to 175.314MB for 14400016-byte allocation
10-16 21:49:03.689 29852-29852/com.klau.jobsinaustralia I/dalvikvm-heap: Grow heap (frag case) to 177.603MB for 3600016-byte allocation
10-16 21:49:03.839 29852-29852/com.klau.jobsinaustralia I/dalvikvm-heap: Grow heap (frag case) to 178.716MB for 14364016-byte allocation
10-16 21:49:04.029 29852-29852/com.klau.jobsinaustralia I/dalvikvm-heap: Grow heap (frag case) to 182.185MB for 14400016-byte allocation
10-16 21:49:04.139 29852-29852/com.klau.jobsinaustralia I/dalvikvm-heap: Forcing collection of SoftReferences for 3600016-byte allocation
10-16 21:49:04.199 29852-29852/com.klau.jobsinaustralia E/dalvikvm-heap: Out of memory on a 3600016-byte allocation.
10-16 21:49:04.199 29852-29852/com.klau.jobsinaustralia I/dalvikvm: "main" prio=5 tid=1 RUNNABLE
10-16 21:49:08.223 29852-29852/com.klau.jobsinaustralia E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.OutOfMemoryError: (Heap Size=194912KB, Allocated=182627KB)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:628)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:475)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:504)
at com.klau.jobsinaustralia.scrollable.CustomList.decodeSampledBitmapFromResource
Activity
public class ExtraInfoActivity extends BaseActivity {
ListView list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
final String languageToLoad = sharedPreferences.getString("PREF_LIST", "no selection");
Locale locale = new Locale(languageToLoad);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,getBaseContext().getResources().getDisplayMetrics());
setContentView(R.layout.activity_extra_info);
String[] nsw_attraction = {
"Sydney Bridge",
"Blue Mountains",
"Bondi Beach",
"The Hunter Valley",
"The Jenolan Caves"
};
final String[] nsw_web = {
getResources().getString(R.string.nsw_attraction_1),
getResources().getString(R.string.nsw_attraction_2),
getResources().getString(R.string.nsw_attraction_3),
getResources().getString(R.string.nsw_attraction_4),
getResources().getString(R.string.nsw_attraction_5)
};
Integer[] nswImageId = {
R.drawable.nsw_attraction_bridge,
R.drawable.nsw_attraction_blue_mountains,
R.drawable.nsw_attraction_bondi_beach,
R.drawable.nsw_attraction_hunter_valley,
R.drawable.nsw_attraction_jenolan_caves
};
CustomList adapter = new CustomList(ExtraInfoActivity.this, nsw_attraction, nsw_web, nswImageId);
list = (ListView) findViewById(R.id.list);
list.setAdapter(adapter);
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ExtraInfoActivity.this, nsw_web[+position], Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onStart() {
super.onStart();
try {
mTracker.setScreenName("Extra Infomation");
mTracker.send(new HitBuilders.AppViewBuilder().build());
}catch (Exception ex){
ex.printStackTrace();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_home, menu);
return true;
}
}
You need to use a library to load the images in the ListView.
Glide is an amazing library to load the images.
It's setup is very simple:
Put these two in your build.gradle:
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:19.1.0'
}
and then in your adapter all you need to do is:
Glide
.with(context)
.load(nswImageId[position])
.into(myImageView);
That's it, it will take care of everything. You can find more information and usage details here.
I have already resized the image. my CustomList Class below
public class CustomList extends ArrayAdapter{
private final Activity context;
private final String[] placeName;
private final String[] web;
private final Integer[] imageId;
public CustomList(Activity context, String[] placeName, String[] web, Integer[] imageId) {
super(context, R.layout.list_single, web);
this.context = context;
this.placeName = placeName;
this.web = web;
this.imageId = imageId;
}
#Override
public View getView(int position, View view, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View rowView= inflater.inflate(R.layout.list_single, null, true);
TextView place = (TextView) rowView.findViewById(R.id.place_name);
place.setText(placeName[position]);
TextView txtTitle = (TextView) rowView.findViewById(R.id.txt);
txtTitle.setText(web[position]);
ImageView imageView = (ImageView) rowView.findViewById(R.id.img);
Bitmap bitmap = decodeSampledBitmapFromResource(context.getResources(), imageId[position], 150, 150);
if (bitmap != null) {
bitmap.recycle(); //<====== ERROR here: java.lang.IllegalArgumentException: Cannot draw recycled bitmap.
}
imageView.setImageBitmap(bitmap);
return rowView;
}
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 = 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;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
}
Use Picasso Library.Its so easy to use and handle many other error also.
http://square.github.io/picasso/
many thanks for a quick answer. Not sure i can do it correctly. please reiew my changes.
#Override
public View getView(int position, View view, ViewGroup parent) {
final ImageView myImageView;
LayoutInflater inflater = context.getLayoutInflater();
View rowView= inflater.inflate(R.layout.list_single, null, true);
TextView place = (TextView) rowView.findViewById(R.id.place_name);
place.setText(placeName[position]);
TextView txtTitle = (TextView) rowView.findViewById(R.id.txt);
txtTitle.setText(web[position]);
ImageView imageView = (ImageView) rowView.findViewById(R.id.img);
if (view == null) {
myImageView = (ImageView) inflater.inflate(imageView, parent, false);
} else {
myImageView = (ImageView) recycled;
}
Glide
.with(context)
.load(nswImageId[position])
.into(myImageView);
return myImageView;
}
write this line in application tag in your manifest file
android:largeHeap="true"
i'm having an issue working with CardViews . I`ve created a CardView containing 1 TextView and 2 ImageViews , which gets displayed in an RecycleView . When i try to update the contents of one of the CardViews (created 5 cards for testing) the interface starts to lag when scrolling over the updated card, but goes back to normal when i pass the item. It only happens when ImageViews are present , when substituting the ImageViews for say, TextViews with some random text in them, it works normally.
Now, here's my code , after that i'll add some more information that could be causing it.
First , the Adapter class, which also contains the items class and List :
public class MovieDetailsAdapter extends RecyclerView.Adapter<MovieDetailsAdapter.MovieViewHolder> {
public List<MovieDetails> movieList;
private static final String TAG = "MyAdapter";
public MovieDetailsAdapter() {
movieList = new ArrayList< MovieDetails>();
}
public void setItemCount(int count ,String title, Bitmap poster,Bitmap fanart ) {
movieList.clear();
movieList.addAll(generateDummyData(count, title, poster , fanart));
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return movieList.size();
}
public void addItem(int position,String title, Bitmap poster, Bitmap fanart) {
if (position > movieList.size()) return;
movieList.add(position, generateDummyItem(title, poster, fanart));
// notifyDataSetChanged();
notifyItemInserted(position);
}
public void updateItem(int position, String title, Bitmap poster, Bitmap fanart) {
if (position > movieList.size()) return;
movieList.add(position, generateDummyItem(title, poster, fanart));
notifyItemChanged(position);
// notifyDataSetChanged();
}
#Override
public MovieViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.card_layout_movies, viewGroup, false);
Log.d(TAG, "create header view holder");
return new MovieViewHolder(itemView);
}
#Override
public void onBindViewHolder(MovieViewHolder movieViewHolder, int i) {
Log.d(TAG, "bind header view holder");
MovieDetails mdet = movieList.get(i);
movieViewHolder.vTitle.setText(mdet.Title);
movieViewHolder.vPoster.setImageBitmap(mdet.imageViewPoster);
movieViewHolder.vFanart.setImageBitmap(mdet.imageViewFanart);
Log.d(TAG, "position: " + i + " holder: " + movieViewHolder + " text: " + movieViewHolder.vTitle);
}
public static class MovieViewHolder extends RecyclerView.ViewHolder {
protected TextView vTitle;
protected ImageView vPoster;
protected ImageView vFanart;
public MovieViewHolder(View v)
{
super(v);
vTitle = (TextView) v.findViewById(R.id.title);
vPoster = (ImageView) v.findViewById(R.id.imageViewPoster);
vFanart = (ImageView) v.findViewById(R.id.imageViewFanart);
}
}
public static class MovieDetails {
protected String title;
protected Bitmap imageViewPoster;
protected Bitmap imageViewFanart;
public MovieDetails(String title, Bitmap imageViewPoster,Bitmap imageViewFanart )
{
this.title = title;
this.imageViewPoster = imageViewPoster;
this.imageViewFanart = imageViewFanart;
}
}
public static MovieDetails generateDummyItem(String title, Bitmap poster, Bitmap fanart) {
MovieDetails mov = new MovieDetails(title, poster, fanart);
return mov;
}
public static List< MovieDetailsAdapter.MovieDetails> generateDummyData(int count, String title , Bitmap imageViewPoster, Bitmap imageviewFanart) {
ArrayList<MovieDetailsAdapter.MovieDetails> items = new ArrayList<MovieDetailsAdapter.MovieDetails>();
for (int i=0; i < count; i++) {
items.add(new MovieDetailsAdapter.MovieDetails(title, imageViewPoster, imageviewFanart));
}
return items;
}
}
Now, here is my main class , the activity class
public class MoviesListActivity extends AppCompatActivity {
public MovieDetailsAdapter ca = new MovieDetailsAdapter();
public RecyclerView recList;
private Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movies_list);
RecyclerView recList = (RecyclerView) findViewById(R.id.cardList);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
button = (Button) findViewById(R.id.button);
MyOnClickListener Listener = new MyOnClickListener();
button.setOnClickListener(Listener);
recList.setItemAnimator(null);
Bitmap bittest1 = decodeSampledBitmapFromResource(getResources(), R.drawable.poster_example, 640, 955);
Bitmap bittest2 = decodeSampledBitmapFromResource(getResources(), R.drawable.fanart_example, 800, 450);
ca.setItemCount(5, "TestingTitle", bittest1, bittest2);
recList.setAdapter(ca);
}
public class MyOnClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
Bitmap bittest12 = decodeSampledBitmapFromResource(getResources(), R.drawable.poster2, 640, 955);
Bitmap bittest22 = decodeSampledBitmapFromResource(getResources(), R.drawable.fanart2, 800, 450);
Toast.makeText(getApplicationContext(), "msg msg", Toast.LENGTH_SHORT).show();
ca.updateItem(1, "test", bittest12, bittest22);
// ca.addItem(1,"test",bittest12,bittest22);
break;
default:
break;
}
}
}
//------methods used for resizing the images down to smaller ones before loading into memory---//
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
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 = 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;
}
And finally, not that i probably matters, my CardView XML.
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
card_view:cardCornerRadius="4dp"
android:layout_margin="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#color/bkg_card"
android:text="Movie Title"
android:gravity="center_vertical"
android:textColor="#android:color/white"
android:textSize="14dp"/>
<ImageView
android:elevation="5dp"
android:scaleType="fitCenter"
android:layout_width="100dp"
android:layout_height="150dp"
android:id="#+id/imageViewPoster"
android:text="Poster"
android:gravity="center_vertical"
android:layout_below="#id/title"
android:layout_marginTop="10dp"
android:layout_marginLeft="5dp"/>
<ImageView
android:id="#+id/imageViewFanart"
android:scaleType="fitCenter"
android:layout_alignBottom="#+id/imageViewPoster"
android:layout_toEndOf="#+id/imageViewPoster"
android:layout_width= "235dp"
android:layout_height="122dp"
android:layout_marginRight="5dp"/>
</RelativeLayout>
Now , somebody in another discussion showed me this as a possible cause : Why RecyclerView.notifyItemChanged() will create a new ViewHolder and use both the old ViewHolder and new one?
So i added the Log items inside the onBind and onCreate methods of the Adapter to see if that could be causing it, and indeed, it`s not just creatind one additional ViewHolders, but 3 of them, and each time i scroll over the newly updated card, it seems to be re-binding, the onBind method seems to be called each time.
Setting the animation to 'null' as per the solution in the thread doesn't seem to have any effect in my case, so maybe that`s not what is causing it.
Any ideas ?
Well, i solved it. It seems to happen only when i'm loading the images via the XML method, if i load them with a 3rd party library like Picasso, the lag seems to dissapear. Something like :
Picasso.with(context).load(MovieDetails.getPoster())
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(movieViewHolder.imageViewPoster);
inside the onBindViewHolder
and instead of passing around bitmaps, just pass a String for the URL or location of the image. And pass the Context to the Adapter class.
I'm am fairly new to android development and ran into an issue where I am trying to create an image array that shows in a gallery and when I click on a picture, it shows the the pic at the bottom. When I run the app, it crashes. Any help i can get will be very very helpful. And thanks in advance.
My questions are
How do I get rid of the NullPointerException?
I'm I decoding the pictures correctly? Can someone show me a better way?
Thanks
My layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".PicturesActivity" >
<Gallery
android:id="#+id/gallery1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="16dp" />
<ImageView
android:id="#+id/image1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/trophykiss" />
</RelativeLayout>
MY CLASS:
public class PicturesActivity extends Activity {
Bitmap[] myImages = new Bitmap[] {
BitmapFactory.decodeResource(getResources(), R.drawable.champions),
BitmapFactory.decodeResource(getResources(), R.drawable.trophykiss),
BitmapFactory.decodeResource(getResources(), R.drawable.championstwo),
BitmapFactory.decodeResource(getResources(), R.drawable.trophies),
BitmapFactory.decodeResource(getResources(), R.drawable.culture),
BitmapFactory.decodeResource(getResources(), R.drawable.maintrophy),
BitmapFactory.decodeResource(getResources(), R.drawable.dive),
BitmapFactory.decodeResource(getResources(), R.drawable.naijamain),
BitmapFactory.decodeResource(getResources(), R.drawable.ethiopia),
BitmapFactory.decodeResource(getResources(), R.drawable.peru),
BitmapFactory.decodeResource(getResources(), R.drawable.funtime),
BitmapFactory.decodeResource(getResources(), R.drawable.skils),
BitmapFactory.decodeResource(getResources(), R.drawable.gabon),
BitmapFactory.decodeResource(getResources(), R.drawable.gambia),
BitmapFactory.decodeResource(getResources(), R.drawable.guinea)
};
private ImageView imageView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pictures);
Gallery g = (Gallery) findViewById(R.id.gallery1);
g.setAdapter(new ImageAdapter(this));
imageView = (ImageView) findViewById(R.id.image1);
g.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "pic: " + position,
Toast.LENGTH_SHORT).show();
imageView.setImageBitmap(myImages[position]);
}
});
}
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
TypedArray a = obtainStyledAttributes(R.styleable.MyGallery);
mGalleryItemBackground = a.getResourceId(
R.styleable.MyGallery_android_galleryItemBackground, 0);
a.recycle();
}
public int getCount() {
return myImages.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView i = new ImageView(mContext);
i.setImageBitmap(myImages[position]);
i.setLayoutParams(new Gallery.LayoutParams(200, 200));
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setBackgroundResource(mGalleryItemBackground);
return i;
}
}
}
ERROR MESSAGE:
java.lang.NullPointerException: Attempt to invoke virtual method
'android.content.res.Resources android.content.Context.getResources()
1)
You can't access your Context object before onCreate() has been called in your current activity. For the way you currently have it to work, just move the initialization of your array into your onCreate() method.
2)
Since you're decoding so many images at once, this should happen on a background thread. Look at the AsyncTask documentation for how to pull the image loading out into a separate thread.
I have developed an app that is suppose to display images located in the drawable folder. I used imageview/viewpager for it. However, I would like to display frame shown below.On the top of the image so that image appears more fancy.. Also, the frame should swipe along with the image...so that it looks more beautiful... I was thinking of creating it permanently on the image...through photoshop... But I didn't like that idea ..So I thought may be android have something for it....I am android beginner...So any code help along with explanation will be appreciated..Following are my codes..
Mainactivity.java
import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
public class MainActivity extends Activity {
MediaPlayer oursong;
ViewPager viewPager;
ImageAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
oursong = MediaPlayer.create(MainActivity.this, R.raw.a);
oursong.seekTo(0);
oursong.start();
viewPager = (ViewPager) findViewById(R.id.view_pager);
adapter = new ImageAdapter(this);
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(MyViewPagerListener);
}
private int pos = 0;
#Override
protected void onPause() {
super.onPause();
if(oursong != null){
pos = oursong.getCurrentPosition();
oursong.release();
oursong = null;
}
}
#Override
protected void onResume(){
super.onResume();
oursong = MediaPlayer.create(MainActivity.this, R.raw.a);
oursong.seekTo(pos); // You will probably want to save an int to restore here
oursong.start();
}
private final OnPageChangeListener MyViewPagerListener = new OnPageChangeListener() {
#Override
public void onPageSelected(int pos) {
if (pos == adapter.getCount() - 1){
// adding null checks for safety
if(oursong != null){
oursong.pause();
}
} else if (!oursong.isPlaying()){
// adding null check for safety
if(oursong != null){
oursong.start();
}
}
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
#Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
};
}
Imageadapter.java
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class ImageAdapter extends PagerAdapter {
Context context;
private int[] GalImages = new int[] {
R.drawable.one,
R.drawable.two,
R.drawable.three,
R.drawable.four,
R.drawable.five
};
ImageAdapter(Context context){
this.context=context;
}
#Override
public int getCount() {
return GalImages.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((ImageView) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = new ImageView(context);
int padding = context.getResources().getDimensionPixelSize(R.dimen.padding_small);
imageView.setPadding(padding, padding, padding, padding);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setImageResource(GalImages[position]);
((ViewPager) container).addView(imageView, 0);
return imageView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((ImageView) object);
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:icon="#drawable/icon" >
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:icon="#drawable/icon" />
<ImageView
android:id="#+id/swipe_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:src="#drawable/swipe_left" />
<ImageView
android:id="#+id/swipe_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="#drawable/swipe_right" />
</RelativeLayout>
Edited
Hidden portion of the image under frame
You can do it using a LayerDrawable
A Drawable that manages an array of other Drawables. These are drawn
in array order, so the element with the largest index will be drawn on
top.
You have two choices to use LayerDrawable.You can either define it in a separate drawable xml and then simply set the image in your ImageView, or you can configure a LayerDrawable dynamically in your code.
Programmatically using code
Resources r = getResources();
Drawable[] layers = new Drawable[2];
layers[0] = r.getDrawable(R.drawable.yourImage);;
layers[1] = r.getDrawable(R.drawable.yourFrame);
LayerDrawable layerDrawable = new LayerDrawable(layers);
imageView.setImageDrawable(layerDrawable);
Now you have your ImageView having two images(1.your image and 2.Frame) set on it.
Edit :
In your ImageAdapter, you need to modify instantiateItem(ViewGroup container, int position) something like
#Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = new ImageView(context);
int padding = context.getResources().getDimensionPixelSize(R.dimen.padding_small);
imageView.setPadding(padding, padding, padding, padding);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
Resources r = context.getResources();
Drawable[] layers = new Drawable[2];
layers[0] = r.getDrawable(GalImages[position]);
layers[1] = r.getDrawable(R.drawable.yourFrame);
LayerDrawable layerDrawable = new LayerDrawable(layers);
imageView.setImageDrawable(layerDrawable);
((ViewPager) container).addView(imageView, 0);
return imageView;
}
Edit 2
As some portions of your image gets hidden under the frame, you need to set the width and height of your image before using it in the ImageView.Have some calculations of what could be the best width and height combination for your image, such that it will fit exactly with your frame.For setting height and width of your image
#Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = new ImageView(context);
int padding = context.getResources().getDimensionPixelSize(R.dimen.padding_small);
imageView.setPadding(padding, padding, padding, padding);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
Resources r = context.getResources();
Bitmap bmp = BitmapFactory.decodeResource(r, GalImages[position]);
int width=200;//set your width
int height=200;//set your height
Bitmap resizedbitmap = Bitmap.createScaledBitmap(bmp, width, height, true);
Drawable d = new BitmapDrawable(r,resizedbitmap);
Drawable[] layers = new Drawable[2];
layers[0] = d;
layers[1] = r.getDrawable(R.drawable.yourFrame);
LayerDrawable layerDrawable = new LayerDrawable(layers);
imageView.setImageDrawable(layerDrawable);
((ViewPager) container).addView(imageView, 0);
return imageView;
}
Using XML
Create a new Drawable XML file, let's call it mylayer.xml:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/yourimage" />
<item android:drawable="#drawable/yourframe" />
</layer-list>
Now in your Activity set the image using that Drawable:
imageView.setImageDrawable(getResources().getDrawable(R.layout.mylayer));
I hope this gives you the basic idea for achieving what you want.