In my widget, I am constantly getting this error:
11-02 09:35:10.613: D/D&D(1557): onCreate called
11-02 09:35:10.933: E/JavaBinder(1557): !!! FAILED BINDER TRANSACTION !!!
11-02 09:35:10.933: D/AppInfoAdapter(1557): top
No matter what I seem to do (I've tried cutting down bitmap sizes, making bitmaps static, commenting out parts of my coding to see where the error was (which didn't help at all by the way)), I always get this error. What this error is causing is that it makes my listView of the users installed applications not show up at all (it's just white space) in a sliding drawer. All my links and other classes work just fine. (But my listView is the center piece and major function of my entire widget.)
I'm at a loss now so I am just going to post the three suspected classes that I've concluded this is steming from (because all the other classes work fine).
Please note that some code is commented out (such as coding for drag and drop functionality because my real device doesn't support honeycomb. I will implement that code when my listView starts working again however)
AppInfoAdapter.java:
package com.example.awesomefilebuilderwidget;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
public class AppInfoAdapter extends BaseAdapter implements Filterable {
private Context mContext;
private List<PackageInfo> mListAppInfo;
private PackageManager mPackManager;
private List<PackageInfo> originalListAppInfo;
private Filter filter;
public AppInfoAdapter(Context c, List<PackageInfo> listApp,
PackageManager pm) {
mContext = c;
this.originalListAppInfo = this.mListAppInfo = listApp;
mPackManager = pm;
Log.d("AppInfoAdapter", "top");
}
#Override
public int getCount() {
Log.d("AppInfoAdapter", "getCount()");
return mListAppInfo.size();
}
#Override
public Object getItem(int position) {
Log.d("AppInfoAdapter", "getItem");
return mListAppInfo.get(position);
}
#Override
public long getItemId(int position) {
Log.d("AppInfoAdapter", "getItemId");
return position;
}
public static Bitmap scaleDownBitmap(Bitmap default_b, int newHeight, Context c) {
final float densityMultiplier = c.getResources().getDisplayMetrics().density;
int h= (int) (100*densityMultiplier);
int w= (int) (h * default_b.getWidth()/((double) default_b.getHeight()));
default_b=Bitmap.createScaledBitmap(default_b, w, h, true);
// TO SOLVE LOOK AT HERE:https://stackoverflow.com/questions/15517176/passing-bitmap-to-other-activity-getting-message-on-logcat-failed-binder-transac
return default_b;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// get the selected entry
final PackageInfo entry = (PackageInfo) mListAppInfo.get(position);
// reference to convertView
View v = convertView;
// inflate new layout if null
if (v == null) {
LayoutInflater inflater = LayoutInflater.from(mContext);
v = inflater.inflate(R.layout.layout_appinfo, null);
Log.d("AppInfoAdapter", "New layout inflated");
}
// load controls from layout resources
ImageView ivAppIcon = (ImageView) v.findViewById(R.id.ivIcon);
TextView tvAppName = (TextView) v.findViewById(R.id.tvName);
TextView tvPkgName = (TextView) v.findViewById(R.id.tvPack);
final CheckBox addCheckbox = (CheckBox) v
.findViewById(R.id.addCheckbox);
Log.d("AppInfoAdapter", "Controls from layout Resources Loaded");
// set data to display
ivAppIcon.setImageDrawable(entry.applicationInfo.loadIcon(mPackManager));
tvAppName.setText(entry.applicationInfo.loadLabel(mPackManager));
tvPkgName.setText(entry.packageName);
Log.d("AppInfoAdapter", "Data Set To Display");
addCheckbox
.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (addCheckbox.isChecked()) {
System.out.println("Checked");
PackageManager pm = mContext.getPackageManager();
Drawable icon = null;
try {
icon = pm
.getApplicationIcon(entry.packageName);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Drawable default_icon = pm.getDefaultActivityIcon();
if (icon instanceof BitmapDrawable
&& default_icon instanceof BitmapDrawable) {
BitmapDrawable icon_bd = (BitmapDrawable) icon;
Bitmap icon_b = icon_bd.getBitmap();
BitmapDrawable default_bd = (BitmapDrawable) pm
.getDefaultActivityIcon();
Bitmap default_b = default_bd.getBitmap();
if (icon_b == default_b) {
// It's the default icon
scaleDownBitmap(default_b, 100, v.getContext());
Log.d("AppInfoAdapter", "Scale Bitmap Chosen");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
default_b.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
Log.d("AppInfoAdapter", "Scale Bitmap to Array");
Intent intent = new Intent(v.getContext(), GridView.class);
intent.putExtra("picture", byteArray);
v.getContext().startActivity(intent);
Log.d("AppInfoAdapter", "Intent started to send Bitmap");
}
}
} else {
System.out.println("Un-Checked");
}
}
});
// return view
return v;
}
#Override
public Filter getFilter() {
if (filter == null) {
filter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<PackageInfo> myFilteredAppList = new ArrayList<PackageInfo>();
constraint = constraint.toString().toLowerCase();
if (constraint.length() == 0) {
myFilteredAppList.addAll(originalListAppInfo);
}
for (PackageInfo appInfo : originalListAppInfo) {
String somefield = appInfo.packageName;
String name = appInfo.packageName;
if (somefield.toLowerCase().contains(
constraint.toString().toLowerCase().toString())
|| (name != null && name.toLowerCase()
.contains(
constraint.toString()
.toLowerCase()
.toString()))) {
myFilteredAppList.add(appInfo);
}
}
results.count = myFilteredAppList.size();
results.values = myFilteredAppList;
return results;
}
#Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
if (results.values != null) {
mListAppInfo = (List<PackageInfo>) results.values;
notifyDataSetChanged();
}
}
};
}
return filter;
}
}
Drag_and_Drop_App (snippet):
public class Drag_and_Drop_App extends Activity {
private static final int SET_BACKGROUND = 10;
private ListView mListAppInfo;
// Search EditText
EditText inputSearch;
public AppInfoAdapter adapter;
final SwipeDetector swipeDetector = new SwipeDetector();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set layout for the main screen
setContentView(R.layout.drag_and_drop_app);
// import buttons
Button btnLinkToPersonalize = (Button) findViewById(R.id.btnLinkToPersonalize);
Log.d("D&D", "onCreate called");
// create new adapter
adapter = new AppInfoAdapter(this, (List<PackageInfo>) Utilities.getInstalledApplications(this), getPackageManager());
// load list application
mListAppInfo = (ListView)findViewById(R.id.lvApps);
// set adapter to list view
mListAppInfo.setAdapter(adapter);
// search bar
inputSearch = (EditText) findViewById(R.id.inputSearch);
inputSearch.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
// When user changed the Text
// Drag_and_Drop_App.this.adapter.getFilter().filter(cs);
if (Drag_and_Drop_App.this.adapter == null){
// some print statement saying it is null
Log.d ("msg_error", "adapter_is_null");
}
Drag_and_Drop_App.this.adapter.getFilter().filter(cs);
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable arg0) {
// TODO Auto-generated method stub
}
});
// implement event when an item on list view is selected
mListAppInfo.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
// get the list adapter
AppInfoAdapter appInfoAdapter = (AppInfoAdapter)parent.getAdapter();
// get selected item on the list
PackageInfo appInfo = (PackageInfo)appInfoAdapter.getItem(pos);
// launch the selected application
Utilities.launchApp(parent.getContext(), getPackageManager(), appInfo.packageName);
}
});
// implement event when an item on list view is selected via long-click
mListAppInfo.setOnItemLongClickListener(new OnItemLongClickListener(){
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view,int pos, long id) {
if (swipeDetector.swipeDetected()){
// do the onSwipe action
} else {
// do the onItemLongClick action
// get the list adapter
AppInfoAdapter appInfoAdapter = (AppInfoAdapter)parent.getAdapter();
// get selected item on the list
PackageInfo appInfo = (PackageInfo)appInfoAdapter.getItem(pos);
// launch the selected application
Utilities.launchApp(parent.getContext(), getPackageManager(), appInfo.packageName);
Log.d("D&D", "App launched");
}
return true;
}
});
// implement slide event to open up plus button
mListAppInfo.setOnTouchListener(swipeDetector);
mListAppInfo.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
if (swipeDetector.swipeDetected()){
// do the onSwipe action
// TEST TO MAKE SURE SWIPE WORKS
AppInfoAdapter appInfoAdapter = (AppInfoAdapter)parent.getAdapter();
// get selected item on the list
PackageInfo appInfo = (PackageInfo)appInfoAdapter.getItem(pos);
// launch the selected application
Utilities.launchApp(parent.getContext(), getPackageManager(), appInfo.packageName);
} else {
// do the onItemClick action
// get the list adapter
AppInfoAdapter appInfoAdapter = (AppInfoAdapter)parent.getAdapter();
// get selected item on the list
PackageInfo appInfo = (PackageInfo)appInfoAdapter.getItem(pos);
// launch the selected application
Utilities.launchApp(parent.getContext(), getPackageManager(), appInfo.packageName);
}
}
});
And finally GridView.java:
package com.example.awesomefilebuilderwidget;
import java.util.ArrayList;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipDescription;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import android.view.View.OnDragListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
public class GridView extends Activity { // implements OnItemLongClickListener, OnDragListener{
ArrayList<Integer> drawables = new ArrayList<Integer>();
private BaseAdapter adapter;
private int draggedIndex = -1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drag_and_drop_app);
Log.d("GridView", "onCreate called");
drawables = new ArrayList<Integer>();
drawables.add(R.drawable.pattern1);
drawables.add(R.drawable.pattern2);
android.widget.GridView gridView = (android.widget.GridView) findViewById(R.id.grid_view);
// gridView.setOnItemLongClickListener(this);
gridView.setAdapter(adapter = new BaseAdapter() {
#Override
// Get a View that displays the data at the specified position in
// the data set.
public View getView(int position, View convertView,
ViewGroup gridView) {
// try to reuse the views.
ImageView view = (ImageView) convertView;
// if convert view is null then create a new instance else reuse
// it
if (view == null) {
view = new ImageView(GridView.this);
}
Bundle extras = getIntent().getExtras();
byte[] byteArray = extras.getByteArray("picture");
Bitmap default_b = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
view.setImageBitmap(default_b);
view.setImageResource(drawables.get(position));
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
view.setLayoutParams(new android.widget.GridView.LayoutParams(70, 70));
view.setTag(String.valueOf(position));
return view;
}
#Override
// Get the row id associated with the specified position in the
// list.
public long getItemId(int position) {
return position;
}
#Override
// Get the data item associated with the specified position in the
// data set.
public Object getItem(int position) {
return drawables.get(position);
}
#Override
// How many items are in the data set represented by this Adapter.
public int getCount() {
return drawables.size();
}
});
}
/*#Override
public boolean onItemLongClick(AdapterView<?> gridView, View view,
int position, long row) {
ClipData.Item item = new ClipData.Item((String) view.getTag());
ClipData clipData = new ClipData((CharSequence) view.getTag(),
new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN }, item);
view.startDrag(clipData, new View.DragShadowBuilder(view), null, 0);
View trashCan = findViewById(R.id.trash_can);
trashCan.setVisibility(View.VISIBLE);
trashCan.setOnDragListener(GridView.this);
trashCan.setOnDragListener(GridView.this);
draggedIndex = position;
return true;
}
#Override
public boolean onDrag(View view, DragEvent dragEvent) {
switch (dragEvent.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
// Drag has started
// If called for trash resize the view and return true
if (view.getId() == R.id.trash_can) {
view.animate().scaleX(1.0f);
view.animate().scaleY(1.0f);
return true;
} else // else check the mime type and set the view visibility
if (dragEvent.getClipDescription().hasMimeType(
ClipDescription.MIMETYPE_TEXT_PLAIN)) {
view.setVisibility(View.GONE);
return true;
} else {
return false;
}
case DragEvent.ACTION_DRAG_ENTERED:
// Drag has entered view bounds
// If called for trash can then scale it.
if (view.getId() == R.id.trash_can) {
view.animate().scaleX(1.5f);
view.animate().scaleY(1.5f);
}
return true;
case DragEvent.ACTION_DRAG_EXITED:
// Drag exited view bounds
// If called for trash can then reset it.
if (view.getId() == R.id.trash_can) {
view.animate().scaleX(1.0f);
view.animate().scaleY(1.0f);
}
view.invalidate();
return true;
case DragEvent.ACTION_DRAG_LOCATION:
// Ignore this event
return true;
case DragEvent.ACTION_DROP:
// Dropped inside view bounds
// If called for trash can then delete the item and reload the grid
// view
if (view.getId() == R.id.trash_can) {
drawables.remove(draggedIndex);
draggedIndex = -1;
}
adapter.notifyDataSetChanged();
case DragEvent.ACTION_DRAG_ENDED:
// Hide the trash can
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
findViewById(R.id.trash_can).setVisibility(View.GONE);
}
}, 1000l);
if (view.getId() == R.id.trash_can) {
view.animate().scaleX(1.0f);
view.animate().scaleY(1.0f);
} else {
view.setVisibility(View.VISIBLE);
}
// remove drag listeners
view.setOnDragListener(null);
return true;
}
return false;
}*/
}
Please comment if you notice ANYTHING that doesn't seem right or that is wrong. I have been trying to fix this error for days and have tried the stackoverflow links here, here, here, and others on top of those.
Please help me to fix this error that I keep getting. I'm really at a loss at what to do now...
(Thanks for reading all of this too, I know it was a lot)
Here is my Utilities class:
package com.example.awesomefilebuilderwidget;
import java.util.List;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
public class Utilities {
/*
* Get all installed application on mobile and return a list
* #param c Context of application
* #return list of installed applications
*/
public static List<PackageInfo> getInstalledApplications(Context c) {
return c.getPackageManager().getInstalledPackages(PackageManager.GET_ACTIVITIES);
}
PackageManager methods, like getInstalledPackages(), can result in a binder transaction error, if the sum total of information being returned is greater than 1MB.
One way to mitigate this is to pass in no flags (e.g., skip PackageManager.GET_ACTIVITIES) to reduce the amount of data in the first call. Then, for those packages where you need the additional detail, call getPackageInfo() to get the details for a specific package. While this does involve multiple IPC round-trips, and therefore is slower, it will help prevent you from blowing out the 1MB-per-transaction limit.
It turned out that I was recieving too much information from the packageManager when I was getting the list of installed applications i.e.
Utilities.getInstalledApplications(this)
In my Utilities class. So then I thought that instead of getting ALL of the installed applications, that I should just get the users installed applications (excluding the system ones that are pointless for my use anyways). Here is the updated class:
public class Utilities {
/*
* Get all installed application on mobile and return a list
* #param c Context of application
* #return list of installed applications
*/
public static List<ResolveInfo> getInstalledApplications(Context c) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
return c.getPackageManager().queryIntentActivities(intent, PackageManager.GET_ACTIVITIES);
}
Then of course any list with
<PackageInfo>
had to be changed to
<ResolveInfo>
but it worked like a charm!
Related
So I am working on a list view, and I want to implement a delete button. But for some reason, whenever I press the delete button on any list item whatsoever, the last item in the list gets deleted automatically. I tried out almost everything, but I am unable to understand how I can know the index of the item where the button was clicked so I can easily just delete that particular item.
This is the code :
// full subtask adapter code
package com.example.taskmasterv3;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
public class SubtaskAdapter extends ArrayAdapter<subtask> {
private final Context context;
private ArrayList<subtask> values;
public SubtaskAdapter(Context context, ArrayList<subtask> list) {
//since your are using custom view,pass zero and inflate the custom view by overriding getview
super(context, 0 , list);
this.context = context;
this.values = list;
}
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
//check if its null, if so inflate it, else simply reuse it
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.subtask_item, parent, false);
}
//use convertView to refer the childviews to populate it with data
TextView tvSubtaskName = convertView.findViewById(R.id.tvSubtaskName);
ImageView ivPri = convertView.findViewById(R.id.ivPri);
ImageView ivTime = convertView.findViewById(R.id.ivTime);
ImageView ivDelete = convertView.findViewById(R.id.ivDelete);
tvSubtaskName.setText(values.get(position).getSubtaskName());
if (values.get(position).isPriHigh()) {
ivPri.setImageResource(R.drawable.priority_high);
} else if (values.get(position).isPriMed()) {
ivPri.setImageResource(R.drawable.priority_med);
} else if (values.get(position).isPriLow()) {
ivPri.setImageResource(R.drawable.priority_low);
}
if (values.get(position).isTimeMore()) {
ivTime.setImageResource(R.drawable.time_symbol_more);
} else if (values.get(position).isTimeMed()) {
ivTime.setImageResource(R.drawable.time_symbol_med);
} else if (values.get(position).isTimeLess()) {
ivTime.setImageResource(R.drawable.time_symbol_less);
}
// Delete button for subtasks (NOT WORKING)
ivDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SubtaskAdapter.this.remove(SubtaskAdapter.this.getItem(position));
notifyDataSetChanged();
}
});
//return the view you inflated
return convertView;
}
//to keep adding the new subtasks try the following
public void addANewSubTask(subtask newSubTask){
ArrayList<subtask> newvalues = new ArrayList<>(this.values);
newvalues.add(newSubTask);
this.values = newvalues;
notifyDataSetChanged();
}
}
ivDelete.setOnClickListener(new View.OnClickListener() {
Change that to:
ivDelete.setOnClickListener(new View.OnClickListener() {
ivDelete.setTag(position);
public void onClick(View v) {
Change to:
public void onClick(View v) {
// int position = v.getTag();
int position = (Integer)v.getTag();
Try to use below code :
ivDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
values .remove(position);
notifyDataSetChanged();
//SubtaskAdapter.this.remove(SubtaskAdapter.this.getItem(position));
//notifyDataSetChanged();
}
});
For the last two days I've been trying to use a ViewModel to store an ArrayList. However, every time I access the ViewModel it initialises the ArrayList, removing the data stored inside it.
I've tried including methods in the ViewModel to control initialisation of the ArrayList, but it hasn't helped. Every time the ViewModel is accessed it resets the ArrayList to 'null.'
Why is this happening and how can I stop it?
ViewModel_Quiz.java
import android.arch.lifecycle.ViewModel;
import java.util.ArrayList;
public class ViewModel_Quiz extends ViewModel {
ArrayList<String> checkboxStatus;
public void sendCheckboxStatus(ArrayList<String> arrayList){
checkboxStatus = arrayList;
}
public ArrayList<String> getCheckboxStatus(){
return checkboxStatus;
}
}
FragmentQuiz_Selection.java
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.support.constraint.Group;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
public class FragmentQuiz_Selection extends Fragment implements View.OnClickListener{
private FloatingActionButton fab_start;
//Create member variable for Checkbox loops
private boolean mChecked;
//Create ArrayList for QuizSelection
private ArrayList<String> mQuizSelection = new ArrayList<>();
//Create boolean for Expand/Collapse
//boolean iv_group_instruments = false;
//Create View variable
private View mView;
//Variables for database
private SQLiteDatabase mInstrumentsDB;
private InstrumentsDbHelper instrumentsDb;
//This contains the same data as mQuizSelection. It's used to prepare the database and populate mTotalRows.
private String[] mQuizSelectionArray;
//This is used as part of quizPrep() to populate mTotalRows. This may note need to be a memberVariable.
private int mTotalRows;
//This contains the total number of columns for the user's selection.
private int mTotalColumns;
//This holds all of the column/row pairs for the quiz.
private ArrayList<String> mQuizList = new ArrayList<>();
//This variable is used to interface with MainActivity
private OnQuizStarted mCallback;
//This ArrayList holds the CheckBox status for restoring the user's state
private ArrayList<String> mCheckboxStatus = new ArrayList<>();
public static FragmentQuiz_Selection newInstance() {
FragmentQuiz_Selection fragment = new FragmentQuiz_Selection();
return fragment;
}
//Create an interface to communicate with MainActivity
public interface OnQuizStarted {
void onQuizStarted (ArrayList<String> arrayList);
}
// Connect the interface to MainActivity
#Override
public void onAttach(Context context) {
super.onAttach(context);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnQuizStarted) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement OnQuizStarted");
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//Store views in variable
mView = inflater.inflate(R.layout.fragment_quiz_selection, container, false);
// Set onClickListeners
setOnClickListeners();
//Ensure all Checkboxes are set to 'false'
ViewGroup constraintLayout = (ViewGroup) mView.findViewById(R.id.quiz_menu_layout);
loopCheckboxesFalse(constraintLayout);
//Create onClickListener for 'start' FAButton
fab_start.setOnClickListener(new View.OnClickListener() {
//If you click the FAButton, start the Quiz
#Override
public void onClick(View view) {
//Create variable for ConstraintLayout
ViewGroup constraintLayout = (ViewGroup) mView.findViewById(R.id.quiz_menu_layout);
quizStart(constraintLayout);
}
});
return mView;
}
//Handle actions when the user is returning to the Fragment
#Override
public void onResume() {
super.onResume();
Log.i("LIFECYCLE CHANGE", "Quiz_Selection, onResume has been called!");
//Get mCheckboxStatus from ViewModel
ViewModel_Quiz viewmodel = ViewModelProviders.of(this).get(ViewModel_Quiz.class);
if (viewmodel.getCheckboxStatus() != null && !(viewmodel.getCheckboxStatus().isEmpty())) {
mCheckboxStatus = viewmodel.getCheckboxStatus();
setCheckboxStatus(mCheckboxStatus);
}
//If mChecked is 'true,' show FAButton. If mChecked is 'false,' hide FAButton.
ViewGroup layout = mView.findViewById(R.id.quiz_menu_layout);
mChecked = loopCheckboxesBoolean(layout);
fabButton(mChecked, fab_start);
}
//Handle actions when the user is leaving, but not necessarily destroying, the Fragment
#Override
public void onPause() {
super.onPause();
Log.i("LIFECYCLE CHANGE", "Quiz_Selection, onPause has been called!");
mCheckboxStatus.clear();
recordCheckboxStatus(mCheckboxStatus);
//Send mCheckboxStatus to ViewModel
ViewModel_Quiz viewmodel = ViewModelProviders.of(this).get(ViewModel_Quiz.class);
viewmodel.sendCheckboxStatus(mCheckboxStatus);
}
#Override
public void onClick(View view) {
if (view instanceof CheckBox) {
onCheckboxClicked(view);
} else if (view instanceof ImageView) {
onExpandCollapse(view);
}
}
//METHOD: Set Checkbox status for 'onCreate'
private void setCheckboxStatus (ArrayList<String> arrayList) {
ViewGroup layout = mView.findViewById(R.id.quiz_menu_layout);
int childCount = layout.getChildCount();
//Iterate through the layout's CheckBox views
for (int i = 0; i < childCount; i++) {
View view = layout.getChildAt(i);
//If item is a Checkbox, assign it as true or false
if (view instanceof CheckBox) {
if (arrayList.get(0) == "y") {
((CheckBox) view).setChecked(true);
arrayList.remove(0);
} else if (arrayList.get(0) == "n") {
((CheckBox) view).setChecked(false);
arrayList.remove(0);
}
}
}
}
//METHOD: Store Checkbox status for 'onPause'
private void recordCheckboxStatus (ArrayList<String> arrayList) {
ViewGroup layout = mView.findViewById(R.id.quiz_menu_layout);
int childCount = layout.getChildCount();
//Iterate through the layout's views
for (int i = 0; i < childCount; i++) {
View view = layout.getChildAt(i);
//If item is a Checkbox && Checkbox is 'checked,' add 'y' to the arrayList.
if (view instanceof CheckBox && ((CheckBox) view).isChecked()) {
arrayList.add("y");
//Else if item is a Checkbox && Checkbox is 'not checked,' add 'n' to the arrayList.
} else if (view instanceof CheckBox && !((CheckBox) view).isChecked()){
arrayList.add("n");
//Else if item is not a Checkbox, skip this view and move to the next one
} else {}
}
}
}
You have to use MutableLiveData in yout ViewModel.
import android.arch.lifecycle.ViewModel;
import java.util.ArrayList;
public class ViewModel_Quiz extends ViewModel {
MutableLiveData<Arraylist<String>> checkboxStatus;
public void sendCheckboxStatus(ArrayList<String> arrayList){
checkboxStatus.postValue(arrayList);
}
public ArrayList<String> getCheckboxStatus(){
return checkboxStatus;
}
}
I am trying to make an app that can render a pdf when someone clicks the specified button. The problem is that whenever I add another pdf and fragment to render, the software just displays the last one I added and not the others. Here is my code:
Main activity
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity {
private View btnRender;
private LinearLayout container;
private View btnRendered;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnRender = (View)findViewById(R.id.btn_render);
container = (LinearLayout)findViewById(R.id.fragment_layout);
btnRendered = (View) findViewById(R.id.btn_rendered);
//set event handling for button
btnRender.setOnClickListener(onClickListener());
btnRendered.setOnClickListener(onClickListener());
}
private View.OnClickListener onClickListener() {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v == btnRender) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_layout, new PDFRenderFragment());
ft.commit();
//gone button after all
btnRender.setVisibility(View.GONE);
container.setVisibility(View.VISIBLE);
} else if (v == btnRendered) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragment_layout, new PDFRenderAgment());
ft.commit();
//gone button after all
btnRender.setVisibility(View.GONE);
container.setVisibility(View.VISIBLE);
}
};
};
}}
First pdf file
import android.app.Fragment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.pdf.PdfRenderer;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import java.io.IOException;
public class PDFRenderFragment extends android.support.v4.app.Fragment {
private ParcelFileDescriptor fileDescriptor;
private PdfRenderer pdfRenderer;
private PdfRenderer.Page currentPage;
private ImageView image;
private Button btnPrevious;
private Button btnNext;
public PDFRenderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_pdfrender, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Retain view references.
image = (ImageView) view.findViewById(R.id.image);
btnPrevious = (Button) view.findViewById(R.id.btn_previous);
btnNext = (Button) view.findViewById(R.id.btn_next);
//set buttons event
btnPrevious.setOnClickListener(onActionListener(-1)); //previous button clicked
btnNext.setOnClickListener(onActionListener(1)); //next button clicked
int index = 0;
// If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
if (null != savedInstanceState) {
index = savedInstanceState.getInt("current_page", 0);
}
showPage(index);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
openRenderer(activity);
} catch (IOException e) {
e.printStackTrace();
Log.i("Fragment", "Error occurred!");
Log.e("Fragment", e.getMessage());
activity.finish();
}
}
#Override
public void onDestroy() {
try {
closeRenderer();
} catch (IOException e) {
e.printStackTrace();
}
super.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (null != currentPage) {
outState.putInt("current_page", currentPage.getIndex());
}
}
/**
* Create a PDF renderer
* #param activity
* #throws IOException
*/
private void openRenderer(Activity activity) throws IOException {
// Reading a PDF file from the assets directory.
fileDescriptor = activity.getAssets().openFd("biology_in_c.pdf").getParcelFileDescriptor();
// This is the PdfRenderer we use to render the PDF.
pdfRenderer = new PdfRenderer(fileDescriptor);
}
/**
* Closes PdfRenderer and related resources.
*/
private void closeRenderer() throws IOException {
if (null != currentPage) {
currentPage.close();
}
pdfRenderer.close();
fileDescriptor.close();
}
/**
* Shows the specified page of PDF file to screen
* #param index The page index.
*/
private void showPage(int index) {
if (pdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != currentPage) {
currentPage.close();
}
//open a specific page in PDF file
currentPage = pdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(currentPage.getWidth(), currentPage.getHeight(),
Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
currentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// showing bitmap to an imageview
image.setImageBitmap(bitmap);
updateUIData();
}
/**
* Updates the state of 2 control buttons in response to the current page index.
*/
private void updateUIData() {
int index = currentPage.getIndex();
int pageCount = pdfRenderer.getPageCount();
btnPrevious.setEnabled(0 != index);
btnNext.setEnabled(index + 1 < pageCount);
getActivity().setTitle(getString(R.string.app_name , index + 1, pageCount));
}
private View.OnClickListener onActionListener(final int i) {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
if (i < 0) {//go to previous page
showPage(currentPage.getIndex() - 1);
} else {
showPage(currentPage.getIndex() + 1);
}
}
};
}
}
Second pdf file
import android.app.Fragment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.pdf.PdfRenderer;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import java.io.IOException;
public class PDFRenderAgment extends android.support.v4.app.Fragment {
private ParcelFileDescriptor fileDescriptor;
private PdfRenderer pdfRenderer;
private PdfRenderer.Page currentPage;
private ImageView image;
private Button btnPrevious;
private Button btnNext;
public PDFRenderAgment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_pdfrender, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Retain view references.
image = (ImageView) view.findViewById(R.id.image);
btnPrevious = (Button) view.findViewById(R.id.btn_previous);
btnNext = (Button) view.findViewById(R.id.btn_next);
//set buttons event
btnPrevious.setOnClickListener(onActionListener(-1)); //previous button clicked
btnNext.setOnClickListener(onActionListener(1)); //next button clicked
int index = 0;
// If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
if (null != savedInstanceState) {
index = savedInstanceState.getInt("current_page", 0);
}
showPage(index);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
openRenderer(activity);
} catch (IOException e) {
e.printStackTrace();
Log.i("Fragment", "Error occurred!");
Log.e("Fragment", e.getMessage());
activity.finish();
}
}
#Override
public void onDestroy() {
try {
closeRenderer();
} catch (IOException e) {
e.printStackTrace();
}
super.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (null != currentPage) {
outState.putInt("current_page", currentPage.getIndex());
}
}
/**
* Create a PDF renderer
* #param activity
* #throws IOException
*/
private void openRenderer(Activity activity) throws IOException {
// Reading a PDF file from the assets directory.
fileDescriptor = activity.getAssets().openFd("biology_in_f.pdf").getParcelFileDescriptor();
// This is the PdfRenderer we use to render the PDF.
pdfRenderer = new PdfRenderer(fileDescriptor);
}
/**
* Closes PdfRenderer and related resources.
*/
private void closeRenderer() throws IOException {
if (null != currentPage) {
currentPage.close();
}
pdfRenderer.close();
fileDescriptor.close();
}
/**
* Shows the specified page of PDF file to screen
* #param index The page index.
*/
private void showPage(int index) {
if (pdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != currentPage) {
currentPage.close();
}
//open a specific page in PDF file
currentPage = pdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(currentPage.getWidth(), currentPage.getHeight(),
Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
currentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// showing bitmap to an imageview
image.setImageBitmap(bitmap);
updateUIData();
}
/**
* Updates the state of 2 control buttons in response to the current page index.
*/
private void updateUIData() {
int index = currentPage.getIndex();
int pageCount = pdfRenderer.getPageCount();
btnPrevious.setEnabled(0 != index);
btnNext.setEnabled(index + 1 < pageCount);
getActivity().setTitle(getString(R.string.app_name , index + 1, pageCount));
}
private View.OnClickListener onActionListener(final int i) {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
if (i < 0) {//go to previous page
showPage(currentPage.getIndex() - 1);
} else {
showPage(currentPage.getIndex() + 1);
}
}
};
}
}
There are no syntax errors in the program, which leads me to believe that there must be some logical explanation why this doesn't work. If anyone could help that would be much appreciated!
Edit due to first answer
That answer did not solve my problem, the last fragment still appeared
Have you tried using this :
public void onClick(View v){
switch (v.getId()) {
case R.id.btn_render:
// do your code
break;
case R.id.btn_rendered:
// do your code
break;
default:
break;
}
}
i am creating images gallery wallpaper app in which i am trying to set image as wallpaper.
when i try to set a image as wallpaper i gets stuck at image loading message.
this is the screenshot: it gets stuck here:
tThis is the set wallpaper code:
set_wallpaper.java
package com.td.gridview;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.WallpaperManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class Set_Wallpaper extends Activity {
private Uri mImageCaptureUri;
private ImageView mImageView;
private AlertDialog dialog;
private static final int PICK_FROM_CAMERA = 1;
private static final int CROP_FROM_CAMERA = 2;
private static final int PICK_FROM_FILE = 3;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.set_wallpaper);
// get intent data
Intent i = getIntent();
// Selected image id
int position = i.getExtras().getInt("id");
ImageView ing = (ImageView) findViewById(R.id.image_page);
ing.setImageResource(MainActivity.ICONS[position]);
doCrop();
}
private void doCrop() {
final ArrayList<CropOption> cropOptions = new ArrayList<CropOption>();
/**
* Open image crop app by starting an intent
* ‘com.android.camera.action.CROP‘.
*/
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setType("image/*");
/**
* Check if there is image cropper app installed.
*/
List<ResolveInfo> list = getPackageManager().queryIntentActivities(
intent, 0);
int size = list.size();
/**
* If there is no image cropper app, display warning message
*/
if (size == 0) {
Toast.makeText(this, "Can not find image crop app",
Toast.LENGTH_SHORT).show();
return;
} else {
/**
* Specify the image path, crop dimension and scale
*/
intent.setData(mImageCaptureUri);
intent.putExtra("outputX", 200);
intent.putExtra("outputY", 200);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
intent.putExtra("return-data", true);
/**
* There is posibility when more than one image cropper app exist,
* so we have to check for it first. If there is only one app, open
* then app.
*/
if (size == 1) {
Intent i = new Intent(intent);
ResolveInfo res = list.get(0);
i.setComponent(new ComponentName(res.activityInfo.packageName,
res.activityInfo.name));
startActivityForResult(i, CROP_FROM_CAMERA);
} else {
/**
* If there are several app exist, create a custom chooser to
* let user selects the app.
*/
for (ResolveInfo res : list) {
final CropOption co = new CropOption();
co.title = getPackageManager().getApplicationLabel(
res.activityInfo.applicationInfo);
co.icon = getPackageManager().getApplicationIcon(
res.activityInfo.applicationInfo);
co.appIntent = new Intent(intent);
co.appIntent
.setComponent(new ComponentName(
res.activityInfo.packageName,
res.activityInfo.name));
cropOptions.add(co);
}
CropOptionAdapter adapter = new CropOptionAdapter(
getApplicationContext(), cropOptions);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Choose Crop App");
builder.setAdapter(adapter,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
startActivityForResult(
cropOptions.get(item).appIntent,
CROP_FROM_CAMERA);
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
if (mImageCaptureUri != null) {
getContentResolver().delete(mImageCaptureUri, null,
null);
mImageCaptureUri = null;
}
}
});
AlertDialog alert = builder.create();
alert.show();
}
}
}
public class CropOption {
public CharSequence title;
public Drawable icon;
public Intent appIntent;
}
public class CropOptionAdapter extends ArrayAdapter<CropOption> {
private ArrayList<CropOption> mOptions;
private LayoutInflater mInflater;
public CropOptionAdapter(Context context, ArrayList<CropOption> options) {
super(context, R.layout.set_wallpaper, options);
mOptions = options;
mInflater = LayoutInflater.from(context);
}
#Override
public View getView(int position, View convertView, ViewGroup group) {
if (convertView == null)
convertView = mInflater.inflate(R.layout.set_wallpaper, null);
CropOption item = mOptions.get(position);
if (item != null) {
((ImageView) convertView.findViewById(R.id.image_page))
.setImageDrawable(item.icon);
((TextView) convertView.findViewById(R.id.tv_name))
.setText(item.title);
return convertView;
}
return null;
}
}
}
The following the code where i am swiping images and sending current showing image to set_wallpaper class on button click
SwipeActivity.java
package com.td.gridview;
import java.io.IOException;
import android.app.Activity;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
public class SwipeActivity extends Activity {
protected int curruntPosition;
protected int hh;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.swipe_view);
// get intent data
Intent i = getIntent();
// Selected image id
final int position = i.getExtras().getInt("id");
final ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
ImagePagerAdapter adapter = new ImagePagerAdapter();
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(position);
viewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
// Here you can set the wallpaper
curruntPosition = arg0;
if (curruntPosition == arg0) {
hh = 1;
}
}
#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
}
});
//
Button bx = (Button) findViewById(R.id.xoom);
bx.setOnClickListener(new View.OnClickListener() {
public void onClick(View vx) {
// // TODO Auto-generated method stub
if (hh == 1) {
// Sending image id to FullScreenActivity
Intent i2 = new Intent(getApplicationContext(),
Full_Zoom.class);
// passing array index
i2.putExtra("id", curruntPosition);
startActivity(i2);
} else {
// get intent data
Intent i3 = getIntent();
// Selected image id
int position = i3.getExtras().getInt("id");
// Sending image id to FullScreenActivity
Intent i2 = new Intent(getApplicationContext(),
Full_Zoom.class);
// passing array index
i2.putExtra("id", position);
startActivity(i2);
}
}
});
//
// set wallpaper on button click
Button b1 = (Button) findViewById(R.id.wll);
b1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v2) {
// // TODO Auto-generated method stub
if (hh == 1) {
// Sending image id to FullScreenActivity
Intent i2 = new Intent(getApplicationContext(),
Set_Wallpaper.class);
// passing array index
i2.putExtra("id", curruntPosition);
startActivity(i2);
} else {
// get intent data
Intent i3 = getIntent();
// Selected image id
int position = i3.getExtras().getInt("id");
// Sending image id to FullScreenActivity
Intent i2 = new Intent(getApplicationContext(),
Set_Wallpaper.class);
// passing array index
i2.putExtra("id", position);
startActivity(i2);
}
}
});
}
public class ImagePagerAdapter extends PagerAdapter {
int[] icons = MainActivity.ICONS;
#Override
public int getCount() {
return icons.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((ImageView) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Context context = SwipeActivity.this;
ImageView imageView = new ImageView(context);
// int padding = context.getResources().getDimensionPixelSize(
// R.dimen.padding_large);
// imageView.setPadding(padding, padding, padding, padding);
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setImageResource(icons[position]);
((ViewPager) container).addView(imageView, 0);
return imageView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((ImageView) object);
}
}
}
I have a simple list view with adapter.
I create 10+ listviewitems dynamically. Then I scroll up and down again and again and again....
I can see that available memory keeps going down...
Where do I need to free and what?
Note - there is an imageview - but in my test I have not used any images so it is View.GONE.
Also - with which tool can I profile the memory usage on the android. I have found yourKit,but how do I configure it for the android (I run the application on the device)/
The Activity class
package org.BJ.Food4All.Activities.NewRecipe;
import org.BJ.Food4All.R;
import org.BJ.Food4All.Recipe;
import org.BJ.Food4All.Recipe.Instruction;
import org.BJ.Food4All.Activities.RecipeBook.RecipeInstructionsListViewAdapter;
import org.BJ.Food4All.Activities.RecipeBook.SharedData;
import org.BJ.Food4All.utils.CameraUtil;
import org.BJ.Food4All.utils.ImageUploadItem;
import android.app.ListActivity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.EditText;
public class Instructions extends ListActivity implements OnClickListener
{
private final static String mTAG = "Instructions";
private EditText mInstructionEditText = null;
private RecipeInstructionsListViewAdapter mListViewAdapter = null;
private Recipe mEditRecipe = PrivateResources.GetRecipe();
private CameraUtil mCameraUtil = new CameraUtil(this);
private int mSelectedEntryIndex = -1;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.new_recipe_instruction_tab);
mInstructionEditText = (EditText)findViewById(R.id.newRecipeInstructionEditTextId);
View addInstructionButton = findViewById(R.id.naddInstructionButtonId);
// Sanity check
if(mInstructionEditText == null || addInstructionButton == null)
{
Log.e(mTAG, "NULL pointers");
// secure exit
finish();
}
// Set up click listeners for all the buttons
addInstructionButton.setOnClickListener(this);
mListViewAdapter = new RecipeInstructionsListViewAdapter(this, R.layout.recipes_instruction_list_single_view_entry, mEditRecipe.GetInstructions());
setListAdapter(mListViewAdapter);
registerForContextMenu(getListView());
}
public void onClick(View v)
{
switch(v.getId())
{
case R.id.naddInstructionButtonId:
AddInstructionToRecipe(v);
break;
default:
Log.e(mTAG, "Invalid ID:" + v.getId());
// secure exit
finish();
}
}
private void AddInstructionToRecipe(View v)
{
String instructionText = mInstructionEditText.getText().toString();
if(instructionText == null)
{
return;
}
Instruction newInstruction = new Instruction( mEditRecipe.GetInstructions().size() + 1, // Index
instructionText, // The instruction
null,
true);
if( mEditRecipe.AddInstruction(newInstruction) != true)
{
// TODO - ERROR
}
else
{
mListViewAdapter.notifyDataSetChanged();
}
}
/*
* (non-Javadoc)
* #see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo)
*/
#Override
public void onCreateContextMenu(ContextMenu menu,
View v,
ContextMenuInfo menuInfo)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.instructions_ctx_menu, menu);
super.onCreateContextMenu(menu, v, menuInfo);
}
/*
* (non-Javadoc)
* #see android.app.Activity#onContextItemSelected(android.view.MenuItem)
*/
#Override
public boolean onContextItemSelected(MenuItem item)
{
super.onContextItemSelected(item);
AdapterView.AdapterContextMenuInfo menuInfo;
menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
mSelectedEntryIndex = menuInfo.position;
switch(item.getItemId())
{
case R.id.deleteId:
mEditRecipe.RemoveInstruction(mSelectedEntryIndex);
mListViewAdapter.notifyDataSetChanged();
return true;
case R.id.takePictureId:
mCameraUtil.TakePicture();
return true;
}
return false;
}
/*
* (non-Javadoc)
* #see android.app.Activity#onActivityResult(int, int, android.content.Intent)
*/
#Override
protected void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
// String imageLocation = mCameraUtil.onActivityResult(requestCode, resultCode, data);
Bitmap imageBitmap = mCameraUtil.onActivityResult(requestCode, resultCode, data);
// TODO - switch to parameter passed in the intent!!!! like TakePicture(index);
// mEditRecipe.GetInstructions().get( mSelectedEntryIndex ).SetBitmap( imageBitmap ); //SetInstructionImageLocation(imageLocation);
mSelectedEntryIndex = -1;
// Update the listviewitem with the picture
mListViewAdapter.notifyDataSetChanged();
}
}
The adapter class
package org.BJ.Food4All.Activities.RecipeBook;
import java.util.ArrayList;
import org.BJ.Food4All.R;
import org.BJ.Food4All.Recipe.Instruction;
import org.BJ.Food4All.utils.GlobalDefs;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class RecipeInstructionsListViewAdapter extends ArrayAdapter<Instruction>
{
private Context mContext;
private ArrayList<Instruction> mItems;
private LayoutInflater mInflater;
public RecipeInstructionsListViewAdapter(Context context, int textViewResourceId, ArrayList<Instruction>items)
{
super(context, textViewResourceId, items);
mContext = context;
mItems = items;
mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View getView(int position,
View convertView,
ViewGroup parent)
{
ViewHolder holder = new ViewHolder();
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.recipes_instruction_list_single_view_entry, null);
}
if(super.getItem(position) != null)
{
holder.instructionIndex = (TextView) convertView.findViewById( R.id.listUp_RecipeInstructionNumberTextBoxId);
holder.instructionText = (TextView) convertView.findViewById( R.id.listUp_RecipeInstructionTextTextBoxId);
holder.instructionImage = (ImageView)convertView.findViewById( R.id.listUp_RecipeInstructionImageViewId);
Typeface tf = Typeface.createFromAsset(mContext.getAssets(), "Eras_Bold.ttf");
holder.instructionIndex.setTypeface(tf);
holder.instructionIndex.setTextSize(30);
holder.instructionIndex.setTextColor(GlobalDefs.GetHeadlineColor());
holder.instructionIndex.setText( Integer.toString(mItems.get(position).getIndex()));
tf = Typeface.createFromAsset(mContext.getAssets(), "Arial.ttf");
holder.instructionText.setTypeface(tf);
holder.instructionText.setTextSize(14);
holder.instructionText.setTextColor(Color.BLACK);
holder.instructionText.setText(mItems.get(position).getText());
Bitmap imageBitmap = mItems.get(position).GetBitmap();
// String imageLocation = mItems.get(position).GetInstructionImageLocation();
if(imageBitmap != null)
{
holder.instructionImage.setImageBitmap(imageBitmap);// setImageURI( Uri.parse(imageLocation ));
holder.instructionImage.setVisibility(View.VISIBLE);
}
else
{
holder.instructionImage.setVisibility(View.GONE);
}
convertView.setTag(holder);
convertView.setLayoutParams(new ListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
}
else
{
}
return convertView;
}
#Override
public boolean isEnabled(int position)
{
return true;
}
static class ViewHolder
{
TextView instructionIndex;
TextView instructionText;
ImageView instructionImage;
}
}
I'm not sure if it is correct to classify this as a bug, but every time you use Typeface.createFromAsset if creates a new font asset and does not release it. See this.
What you can do is load the typefaces when you load your app and reference them statically. I put my typefaces in Application.
public class YourApp extends android.app.Application {
public void onCreate() {
super.onCreate();
// typeface caching
initializeTypefaces();
}
public static class Fonts {
public static Typeface THEOREM;
}
private void initializeTypefaces(){
Fonts.THEOREM = Typeface.createFromAsset(getAssets(), "fonts/theorem.otf");
}
}
And then I do this in my adapter:
textView.setTypeface(YourApp.Fonts.THEOREM);
You can go here to see how to use Application in Android.
Lastly, it looks like your still creating your ViewHolder every time instead of only when convertView is null. I would review this video to get the whole picture. http://www.google.com/events/io/2010/sessions/world-of-listview-android.html
Here is an example of how I use the ViewHolder method:
#Override
public View getView(int pos, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null || convertView.getTag() == null){
convertView = inflater.inflate(R.layout.list_item, parent, false);
holder = new ViewHolder();
holder.text1 = (TextView)convertView.findViewById(R.id.list_item_text1);
holder.text2 = (TextView)convertView.findViewById(R.id.list_item_text2);
holder.text1.setTypeface(YourApp.Fonts.THEOREM); // only happens once when recycling!
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.text1.setText("someText");
holder.text2.setText("someText");
return convertView;
}
You can try to free the resources if convertView != null. if it is you could find your Views and null them. You could also try to get the Bitmap of your ImageView and recycle it.
you could also add your ViewHolder as a member to your RecipeInstructionsListViewAdapter and instantiate it once in constructor.