I have a FragmentA. When I click on a button in FragmentA I go to FragmentB. In FragmentB I have a PopupWindow. The PopupWindow have a ViewPager with two pages.
I took help from this code - Emojicon
I have 2 separate classes, View1 and View2, for the views at page 1 and 2 of the ViewPager respectively. Both these classes, View1 and View2, extends a parent class ViewBase.
Here is my problem:
Scenario 1: When I am at FragmentA the memory graph shows 13MB utilization. When I go to FragmentB without showing PopupWindow the memory graph shows 16MB and when I come back to FragmentA it comes down to 13MB. This is good.
Scenario 2: When I am at FragmentA the memory graph shows 13MB utilization. When I go to FragmentB with showing PopupWindow the memory graph shows 20MB and when I come back to FragmentA it doesn't come down to 13MB.
I have tried Eclipse MAT and Heap dump to find out the issue but still no help. I can see in the MAT that FragmentB is still in memory when I come back to FragmentA holding the instances of PopupWindow, View1 and View2. None of them are released. FragmentB should not be in memory.
Please help me out.
Here is my DemoPopupWindow.java
public class DemoPopupWindow extends PopupWindow {
// Views
private TabLayout mTabLayout;
private CustomViewPager mViewPager;
private PagerAdapter mViewPagerAdapter;
private RelativeLayout mLayout;
private View mRootView;
// Variables
private int mGreyColor, mPrimaryColor;
private OnSoftKeyboardOpenCloseListener onSoftKeyboardOpenCloseListener;
private int keyBoardHeight = 0;
private Boolean pendingOpen = false;
private Boolean isOpened = false;
private Context mContext;
ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
mRootView.getWindowVisibleDisplayFrame(r);
int screenHeight = mRootView.getRootView().getHeight();
int heightDifference = screenHeight - (r.bottom);
if (heightDifference > 100) {
keyBoardHeight = heightDifference;
setSize(WindowManager.LayoutParams.MATCH_PARENT, keyBoardHeight);
if (isOpened == false) {
if (onSoftKeyboardOpenCloseListener != null)
onSoftKeyboardOpenCloseListener.onKeyboardOpen(keyBoardHeight);
}
isOpened = true;
if (pendingOpen) {
showAtBottom();
pendingOpen = false;
}
} else {
isOpened = false;
if (onSoftKeyboardOpenCloseListener != null)
onSoftKeyboardOpenCloseListener.onKeyboardClose();
}
}
};
/**
* Constructor
* #param rootView
* #param mContext
*/
public DemoPopupWindow(View rootView, Context mContext){
super(mContext);
this.mContext = mContext;
this.mRootView = rootView;
Resources resources = mContext.getResources();
View customView = createCustomView(resources);
setContentView(customView);
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
setSize((int) mContext.getResources().getDimension(R.dimen.keyboard_height), WindowManager.LayoutParams.MATCH_PARENT);
}
/**
* Set keyboard close listener
* #param listener
*/
public void setOnSoftKeyboardOpenCloseListener(OnSoftKeyboardOpenCloseListener listener){
this.onSoftKeyboardOpenCloseListener = listener;
}
/**
* Show PopupWindow
*/
public void showAtBottom(){
showAtLocation(mRootView, Gravity.BOTTOM, 0, 0);
}
/**
* Show PopupWindow at bottom
*/
public void showAtBottomPending(){
if(isKeyBoardOpen())
showAtBottom();
else
pendingOpen = true;
}
/**
* Check whether keyboard is open or not
* #return
*/
public Boolean isKeyBoardOpen(){
return isOpened;
}
/**
* Set soft keyboard size
*/
public void setSizeForSoftKeyboard(){
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
/**
* Remove global layout listener
*/
public void removeGlobalListener() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mRootView.getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
} else {
mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
}
}
/**
* Set PopupWindow size
* #param width
* #param height
*/
public void setSize(int width, int height){
keyBoardHeight = height;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, keyBoardHeight);
mLayout.setLayoutParams(params);
setWidth(width);
setHeight(height);
}
/**
* Create PopupWindow View
* #return
*/
private View createCustomView(Resources resources) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.popup, null, false);
mViewPager = (CustomViewPager) view.findViewById(R.id.pager);
mLayout = (RelativeLayout) view.findViewById(R.id.layout);
mViewPagerAdapter = new ViewPagerAdapter(
Arrays.asList(
new View1(mContext, this),
new View2(mContext, this)
)
);
mViewPager.setAdapter(mViewPagerAdapter);
mPrimaryColor = resources.getColor(R.color.color_primary);
mGreyColor = resources.getColor(R.color.grey_color);
mTabLayout = (TabLayout) view.findViewById(R.id.tabs);
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.setupWithViewPager(mViewPager);
return view;
}
/**
* ViewPager Adapter
*/
private static class ViewPagerAdapter extends PagerAdapter {
private List<ViewBase> views;
public ViewPagerAdapter(List<ViewBase> views) {
super();
this.views = views;
}
#Override
public int getCount() {
return views.size();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
View v = views.get(position).mRootView;
((ViewPager)container).addView(v, 0);
return v;
}
#Override
public void destroyItem(ViewGroup container, int position, Object view) {
((ViewPager)container).removeView((View)view);
}
#Override
public boolean isViewFromObject(View view, Object key) {
return key == view;
}
}
/**
* Soft keyboard open close listener
*/
public interface OnSoftKeyboardOpenCloseListener{
void onKeyboardOpen(int keyBoardHeight);
void onKeyboardClose();
}
}
Please note that I haven't pasted complete PopupWindow class here but only the necessary part.
Here is how I am using this DemoPopupWindow in my FragmentB
mPopupWindow = new DemoPopupWindow(mLayout, getActivity());
mPopupWindow.setSizeForSoftKeyboard();
// If the text keyboard closes, also dismiss the PopupWindow
mPopupWindow.setOnSoftKeyboardOpenCloseListener(new DemoPopupWindow.OnSoftKeyboardOpenCloseListener() {
#Override
public void onKeyboardOpen(int keyBoardHeight) {
}
#Override
public void onKeyboardClose() {
if (mPopupWindow.isShowing())
mPopupWindow.dismiss();
}
});
In FragmentB onDestroy I am calling this method to remove GlobalLayoutListener
mPopupWindow.removeGlobalListener();
I have a button in FragmentB to show and dismiss PopupWindow.
Here is my ViewBase.java
public class ViewBase {
public View mRootView;
DemoPopupWindow mPopup;
private Context mContext;
public ViewBase (Context context, DemoPopupWindow popup) {
mContext = context;
mPopup = popup;
}
public ViewBase () {
}
}
Here is my View1
public class View1 extends ViewBase{
// Views
public View mRootView;
DemoPopupWindow mPopup;
private LinearLayout mLayoutText;
// Variables
private Context mContext;
private List<String> mText;
/**
* Constructor
*/
public View1(Context context, DemoPopupWindow popup) {
super(context, popup);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
mPopup = popup;
mRootView = inflater.inflate(R.layout.fragment_view1, null);
mContext = context;
// Set parent class rootview
super.mRootView = mRootView;
registerViews(mRootView);
registerListeners();
populateText();
}
/**
* Register all the views
* #param view
*/
private void registerViews(View view) {
mLayoutText = (LinearLayout) view.findViewById(R.id.view1_layout);
mText = TextManager.getInstance().getText();
}
/**
* Populate text
*/
private void populateText() {
int length = mText.size();
for(int i=0; i<length; i++) {
addNewText(mText.get(i).getText());
}
}
/**
* Add new text
* #param text
*/
private void addNewText(final String text) {
TextView textView = createTextView(text);
mLayoutText.addView(textView);
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do something
}
});
}
/**
* Create textview
* #param text
* #return
*/
private TextView createTextView(final String text) {
TextView textView = new TextView(mContext);
FlowLayout.LayoutParams params = new FlowLayout.LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT, 40);
params.setMargins(4, 4, 0, 0);
textView.setLayoutParams(params);
textView.setClickable(true);
textView.setGravity(Gravity.CENTER);
textView.setPadding(10, 0, 10, 0);
textView.setText(text);
textView.setTextSize(20);
return textView;
}
}
EDIT AGAIN:
I have found the issue but I dont know how to fix it. The problem is with mGlobalLayoutListener. This is holding the reference of some view. If I don't use GlobalLayoutListener at all then the FragmentB instance is getting removed from the memory.
Even after calling removeGlobalLayout(), this listener is not getting released. Please help me out.
are you sure CustomPopupWindow is causing you memory leak? Have you done garbage collection before running heap dump, maybe there is no leak at all..?
It's called onDestroy in FragmentB with popup when you goes back to fragmentA?
How to remove safely GlobalLayoutListener ?
Caution of your Android version, since api is deprecated! :)
Can you try this
if (Build.VERSION.SDK_INT < 16) {
v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
} else {
v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
Related
I want to use ViewStub in keyboard.xml
I have a layout which has a key of the keyboard, see screenshot
when I am going to open keyboard it takes some time to open because of loading this much views.
For that I used ViewStub, but it will not reduce time to open keyboard
someone, please help to sort out this, how can I reduce the loading time of these such bunch of views
CODE
<com.lingapps.appwriter.keyboard.internal.KeyView
android:id="#+id/key_q"
style="#style/keyboard_key_view_mobile"
android:onClick="#{clickListener::onClick}"
android:onLongClick="#{longClickListener::onLongClick}"
custom:code="#integer/q"
custom:layout_constraintLeft_toLeftOf="parent"
custom:layout_constraintRight_toLeftOf="#id/key_w"
custom:layout_constraintTop_toTopOf="parent"
custom:title="#{isShifted?Character.toUpperCase(#integer/q):Character.toLowerCase(#integer/q)}" />
for each and every key I used above view
and here I am inflating a layout
public class KeyboardView extends LinearLayout implements OnTouchListener, View.OnClickListener, View.OnLongClickListener {
private KeyboardQwertyBinding mBindingQwerty;
private KeyboardSymbolsBinding mBindingSymbol;
private KeyboardSymbolsShiftBinding mBindingSymbolShift;
private ObservableBoolean languageOtherKeyVisibility = new ObservableBoolean(false);
private ObservableBoolean isShiftedObservableBoolean = new ObservableBoolean(false);
private ObservableInt languageKeyObservable1 = new ObservableInt(0);
private ObservableInt languageKeyObservable2 = new ObservableInt(0);
private ObservableInt languageKeyObservable3 = new ObservableInt(0);
private Handler handlerDeleteButton = new Handler();
private int codeDeleteButton;
public boolean keyboardChanged = false;
private boolean previousShift;
private CandidateView mCandidateView;
private RelativeLayout rlCandidateView;
public enum KeyboardType {
QWERTY, SYMBOLS, SYMBOLS_SHIFT
}
public enum KeyboardLocale {
ENGLISH, DANISH, SWEDISH, ENGLISH_US, AUSTRALIAN, DUTCH, GERMAN, SPANISH, FRENCH, BOKMAL, NYNORSK
}
private KeyboardType mKeyboardType = KeyboardType.QWERTY;
private KeyboardLocale mKeyboardLocale = KeyboardLocale.DANISH;
private Keyboard keyboard;
private Context context;
public Keyboard getKeyboard() {
return keyboard;
}
public CandidateView setKeyboard(Keyboard keyboard, boolean languageChange, EditorInfo attribute) {
this.keyboard = keyboard;
this.mKeyboardType = keyboard.getType();
if (keyboard.getLocale() != null) {
this.mKeyboardLocale = keyboard.getLocale();
}
keyboard.setShiftStateListener(shiftListener);
makeKeyboard(languageChange, attribute);
previousShift = false;
return mCandidateView;
}
public KeyboardView(Context context) {
super(context);
this.context = context;
appPreferences = new AppPreferences(context);
setOrientation(LinearLayout.VERTICAL);
setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
public KeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
setOrientation(LinearLayout.VERTICAL);
setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
}
/**
* make keyboard for
*
* #param languageChange reinitialize keyboard if language change
* #param attribute editor info
*/
protected void makeKeyboard(boolean languageChange, EditorInfo attribute) {
if (keyboard == null || mKeyboardType == null)
return;
ConstraintLayout linearLayout;
if (mKeyboardType.equals(KeyboardType.SYMBOLS)) {
if (mBindingSymbol == null)
prepareKeyboardLayoutSymbol(attribute);
linearLayout = (ConstraintLayout) mBindingSymbol.getRoot();
} else if (mKeyboardType.equals(KeyboardType.SYMBOLS_SHIFT)) {
if (mBindingSymbolShift == null)
prepareKeyboardLayoutShiftSymbol(attribute);
linearLayout = (ConstraintLayout) mBindingSymbolShift.getRoot();
} else {
if (languageChange && mBindingQwerty == null)
prepareKeyboardLayoutQwerty(attribute);
linearLayout = (ConstraintLayout) mBindingQwerty.getRoot();
}
if (rlCandidateView == null) {
rlCandidateView = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.candidate_view, new RelativeLayout(context));
}
if (mCandidateView == null) {
if (appWriterSoftKeyboard == null) {
appWriterSoftKeyboard = (AppWriterSoftKeyboard) context;
}
mCandidateView = new CandidateView(context, rlCandidateView);
mCandidateView.setSoftDanishKeyboard(appWriterSoftKeyboard);
}
removeAllViewsInLayout();
setOrientation(VERTICAL);
addView(rlCandidateView);
addView(linearLayout);
invalidate();
requestLayout();
}
/**
* prepare layout for qwerty keyboard
*
* #param attribute editor info
*/
#SuppressLint("ClickableViewAccessibility")
private void prepareKeyboardLayoutQwerty(EditorInfo attribute) {
mBindingQwerty = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.keyboard_qwerty, null, false);
mBindingQwerty.setLanguageOtherKeyVisibility(languageOtherKeyVisibility);
mBindingQwerty.setIsShifted(isShiftedObservableBoolean);
mBindingQwerty.setLanguageKey1(languageKeyObservable1);
mBindingQwerty.setLanguageKey2(languageKeyObservable2);
mBindingQwerty.setLanguageKey3(languageKeyObservable3);
getKeyboardLayout();
mBindingQwerty.setClickListener(KeyboardView.this);
mBindingQwerty.setLongClickListener(KeyboardView.this);
mBindingQwerty.keyBackspace.setOnTouchListener(KeyboardView.this);
setImeOption(attribute, mBindingQwerty.keyEnter);
}
public boolean isShifted() {
return getKeyboard().isShifted();
}
public void setShifted(boolean b) {
if (getKeyboard() != null)
getKeyboard().setShifted(b);
}
private ShiftStateListener shiftListener = new ShiftStateListener() {
#Override
public void onShiftChange(boolean isShifted) {
if (getKeyboard() == null)
return;
if (previousShift != isShifted) {
isShiftedObservableBoolean.set(isShifted);
previousShift = isShifted;
}
}
};
}
This issue is solved, and now no need to use ViewStub
I have replaced TextView instead of com.lingapps.appwriter.keyboard.internal.KeyView now it works fastly and didn't take time to load
Native TextView is faster than customView, so I replaced all key with native TextView
i was facing the problem to modified views, project is using carousel view but now need to change to custom listview
from this public class ProfileControlFragment extends Fragment { carousel view begin
public class ProfileControlFragment extends Fragment {
....
private CarouselPagerAdapter mAdapter;
/**
* Setting the CarouselView with data
*/
private void setCarouselView() {
// Getting the number of services discovered
mPages = ServiceDiscoveryFragment.mGattServiceData.size();
FIRST_PAGE = mPages * LOOPS / 2;
// Setting the adapter
mAdapter = new CarouselPagerAdapter(getActivity(),
ProfileControlFragment.this, getActivity()
.getSupportFragmentManager(),
ServiceDiscoveryFragment.mGattServiceData);
mPager.setAdapter(mAdapter);
mPager.setOnPageChangeListener(mAdapter);
mPager.setCurrentItem(FIRST_PAGE);
mPager.setOffscreenPageLimit(3);
if (mPages == 0) {
Toast.makeText(getActivity(), getResources().getString(R.string.toast_no_services_found), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), getResources().getString(R.string.toast_swipe_profiles), Toast.LENGTH_SHORT).show();
}
}
this the carousel adapter
public class CarouselPagerAdapter extends FragmentPagerAdapter implements
ViewPager.OnPageChangeListener{
....
private ArrayList<HashMap<String, BluetoothGattService>> mCurrentServiceData;
public CarouselPagerAdapter(Activity context,
ProfileControlFragment containerFragment,
FragmentManager fragmentManager,
ArrayList<HashMap<String, BluetoothGattService>> currentServiceData) {
super(fragmentManager);
this.mFragmentManager = fragmentManager;
this.mContext = (HomePageActivity) context;
this.mContainerFragment = containerFragment;
this.mCurrentServiceData = currentServiceData;
}
//passing data to the current fragments
Fragment curFragment = CarouselFragment.newInstance(imageId,
mScale, name, uuid.toString(), bgs);
return curFragment;
}
and show that data ini this class
public class CarouselFragment extends Fragment {
.....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(
R.layout.carousel_fragment_item, container, false);
final int pos = this.getArguments().getInt(EXTRA_FRAG_POS);
final String mName = this.getArguments().getString(EXTRA_FRAG_NAME);
final String mUuid = this.getArguments().getString(EXTRA_FRAG_UUID);
TextView mTv = (TextView) rootView.findViewById(R.id.text);
mTv.setText(mName);
if (mName.equalsIgnoreCase(getResources().getString(
R.string.profile_control_unknown_service))) {
mService = mBleHashMap.get(mUuid);
mCurrentUUID = mService.getUuid();
UUID UUID_TEMPERATURE_SERVICE = UUID.fromString(TEMPERATURE_SERVICE);
if (mCurrentUUID==UUID_TEMPERATURE_SERVICE)
{
mTv.setText("Temperature service");
}
TextView mTvUUID = (TextView) rootView.findViewById(R.id.text_uuid);
mTvUUID.setText(mCurrentUUID.toString());
}
please help me to change this to custom listview
I want to develop the UI in which user will touch the touch pad and if he is registered user of the system , he will be verified by my app. So to indicate that I want to change the fingerprint image by green (valid user) , red (invalid user). So I have created the fragment in which I am getting that result in following two methods
#Override
public void authenticate() {
Log.d(TAG, "authoticate: ");
result = "Yes";
//customBuilder.setImage(R.drawable.ic_fingerprint_pressed);//tried but not working
}
#Override
public void errorAuthenticate() {
Log.d(TAG, "fail: ");
result = "No";
// customBuilder.setImage(R.drawable.ic_fingerprint_pressed_error);//tried but not working
}
Now in same fragment I have created the CustomDialog to show the above images which will be change dynamically. Code for CustomDialog class is given below
public class CustomDialog extends AlertDialog {
public CustomDialog(Context context, int theme) {
super(context, theme);
}
public CustomDialog(Context context) {
super(context);
}
/**
* Helper class for creating a custom dialog
*/
public static class Builder {
private Context context;
private String title;
private int res;
private String message;
private String positiveButtonText;
private String negativeButtonText;
private View contentView;
private DialogInterface.OnClickListener
positiveButtonClickListener,
negativeButtonClickListener;
public Builder(Context context) {
this.context = context;
}
public Builder setMessage(String message) {
this.message = message;
return this;
}
public Builder setMessage(int message) {
this.message = (String) context.getText(message);
return this;
}
/**
* Set the Dialog title from resource
* #param title
* #return
*/
public Builder setTitle(int title) {
this.title = (String) context.getText(title);
return this;
}
/**
* Set the Dialog title from String
* #param title
* #return
*/
public Builder setTitle(String title) {
this.title = title;
return this;
}
public Builder setImage(int res){
this.res = res;
return this;
}
/**
* Set a custom content view for the Dialog.
* If a message is set, the contentView is not
* added to the Dialog...
* #param v
* #return
*/
public Builder setContentView(View v) {
this.contentView = v;
return this;
}
/**
* Set the positive button resource and it's listener
* #param positiveButtonText
* #param listener
* #return
*/
public Builder setPositiveButton(int positiveButtonText,
DialogInterface.OnClickListener listener) {
this.positiveButtonText = (String) context
.getText(positiveButtonText);
this.positiveButtonClickListener = listener;
return this;
}
/**
* Set the positive button text and it's listener
* #param positiveButtonText
* #param listener
* #return
*/
public Builder setPositiveButton(String positiveButtonText,
DialogInterface.OnClickListener listener) {
this.positiveButtonText = positiveButtonText;
this.positiveButtonClickListener = listener;
return this;
}
/**
* Set the negative button resource and it's listener
* #param negativeButtonText
* #param listener
* #return
*/
public Builder setNegativeButton(int negativeButtonText,
DialogInterface.OnClickListener listener) {
this.negativeButtonText = (String) context
.getText(negativeButtonText);
this.negativeButtonClickListener = listener;
return this;
}
/**
* Set the negative button text and it's listener
* #param negativeButtonText
* #param listener
* #return
*/
public Builder setNegativeButton(String negativeButtonText,
DialogInterface.OnClickListener listener) {
this.negativeButtonText = negativeButtonText;
this.negativeButtonClickListener = listener;
return this;
}
/**
* Create the custom dialog
*/
public CustomDialog create() {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// instantiate the dialog with the custom Theme
final CustomDialog dialog = new CustomDialog(context,
R.style.Dialog);
View layout = inflater.inflate(R.layout.capture_finger_touch, null);
// dialog.addContentView(layout, new ViewGroup.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
// set the dialog title
((ImageView) layout.findViewById(R.id.imgView)).setImageResource(res);
// set the confirm button
if (positiveButtonText != null) {
((Button) layout.findViewById(R.id.btn))
.setText(positiveButtonText);
if (positiveButtonClickListener != null) {
((Button) layout.findViewById(R.id.btn))
.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
positiveButtonClickListener.onClick(
dialog,
DialogInterface.BUTTON_POSITIVE);
}
});
}
} else {
// if no confirm button just set the visibility to GONE
layout.findViewById(R.id.btn).setVisibility(
View.GONE);
}
dialog.setView(layout);
return dialog;
}
}
}
And I have used above CustomDialog by following way
public static String result = "No Found";
CustomDialog.Builder customBuilder = new CustomDialog.Builder(getActivity());
public void captureFingerPrintTouchCustom() {
if (result.equalsIgnoreCase("Yes")) {
customBuilder.setImage(R.drawable.ic_fingerprint_pressed);
} else if (result.equalsIgnoreCase("No")) {
customBuilder.setImage(R.drawable.ic_fingerprint_pressed_error);
//rl.startAnimation(animation);
} else customBuilder.setImage(R.drawable.ic_fingerprint_for_capture);
customBuilder.setPositiveButton("OK2", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog = customBuilder.create();
alertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
alertDialog.setCanceledOnTouchOutside(false);
alertDialog.setCancelable(false);
alertDialog.show();
}
Here as per the result value I want to change the image of customdialog.
capture_finger_touch.xml code is given below
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="#+id/rl"
android:layout_width="#dimen/alert_dialog_size"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="#drawable/rounded_white_background"
android:padding="#dimen/view_internal_space">
<ImageView
android:id="#+id/imgView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:background="#drawable/key_bg_square"
android:src="#drawable/ic_fingerprint_for_capture" />
<Button
android:id="#+id/btn"
style="#style/button_style"
android:layout_height="#dimen/btnHeight"
android:layout_width="wrap_content"
android:layout_below="#+id/imgView"
android:layout_centerHorizontal="true"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:textColor="#color/white"
android:textSize="#dimen/BTC_title_size"
android:textStyle="bold" />
</RelativeLayout>
</RelativeLayout>
But problem is that its not changing the image dynamically. CustomDialog is created like this,
I would suggest you to extend DialogFragment.class, inflate there you layout and communicate with it in you Activity or Fragment
public class FingerprintDialog extends AppCompatDialogFragment implements View.OnClickListener {
private DialogFingerprintBinding binding;
private Listener listener;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
listener = (Listener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " implement Listener");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//if you use databinding
//binding = DataBindingUtil.inflate(LayoutInflater.from(getContext()), R.layout.XXX, null, false);
//binding.buttonCancel.setOnClickListener(this);
View view = inflater.inflate(R.layout.XXX, container, false);
Button button = view.findViewById(R.id.button);
getDialog().setCanceledOnTouchOutside(false);
if (getDialog().getWindow() != null) {
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
}
//return binding.getRoot(); for binding
return view;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
listener.cancel();
break;
default:
break;
}
}
public void changeImage(//posible resource) {
//change Image here
}
public interface Listener {
void cancel();
}
}
And then create instance of this dialog nad show it using method DialogFragment#show
You need to setImageResource to ImageView as follows:
private fingerPrintImageView;
public CustomDialog create() {fingerPrintImageView = ((ImageView) layout.findViewById(R.id.imgView)).setImageResource(res);}
public Builder setImage(int res){this.res = res;fingerPrintImageView.setImageResource(res); return this;}
I am having a ViewPager which works well, but having a memory leak in it.
I tried to find the leak with the heap analysis and with Eclipse memory analyser.
It turns out I got several ViewPager instances and not released Bitmaps in the heap after several activity recreations.
Where can be the leak reason in my code?
Here is the Fragment, in which I use ViewPager and scroll items after some period of time:
private final int INTERVAL_TIME = 15000;
private ViewPager mViewPager;
private ViewPagerAdapter mAdapter;
private Handler mHandler;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bottom, container, false);
......
mViewPager = (ViewPager) view.findViewById(R.id.pager);
mAdapter = new ViewPagerAdapter(getActivity(), bottomItems);
mViewPager.setAdapter(mAdapter);
mHandler = new Handler();
mHandler.postDelayed(UpdateTimeThread, INTERVAL_TIME);
return view;
}
private Runnable UpdateTimeThread = new Runnable() {
#Override
public void run() {
int position;
if (mViewPager.getCurrentItem() == mViewPager.getAdapter().getCount() - 1) {
position = 0;
} else {
position = mViewPager.getCurrentItem() + 1;
}
mViewPager.setCurrentItem(position, true);
mHandler.postDelayed(this, INTERVAL_TIME);
}
};
And here is my PagerAdapter which can show simultaneously 3 items on the screen (if their format.equals("1")) or 2 items at a time in case one of them is double sized (format.equals("2"))
public class ViewPagerAdapter extends PagerAdapter {
private final List<JsonParsed.BottomItem> bottomItems;
private Activity activity;
public ViewPagerAdapter(Activity activity, List<JsonParsed.BottomItem> bottomItems) {
this.activity = activity;
this.bottomItems = bottomItems;
}
#Override
public int getCount() {
if (bottomItems.size() < 3) {
return 1;
}
return (int) Math.floor((float) bottomItems.size() / 3f);
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
#Override
public Object instantiateItem(ViewGroup container, final int position) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View itemView = inflater.inflate(R.layout.pager_item, container, false);
ImageView pic = (ImageView) itemView.findViewById(R.id.image_item);
ImageView pic2 = (ImageView) itemView.findViewById(R.id.image_item2);
ImageView pic3 = (ImageView) itemView.findViewById(R.id.image_item3);
pic.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
click(bottomItems.get(position * 3));
}
});
pic2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
click(bottomItems.get(position * 3 + 1));
}
});
pic3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
click(bottomItems.get(position * 3 + 2));
}
});
if (bottomItems.get(position * 3) != null) {
pic.setImageBitmap(BitmapFactory.decodeFile(bottomItems.get(position * 3).getPrev(activity).getAbsolutePath()));
if (bottomItems.get(position * 3).format.equals("1")) {
pic.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 2f));
} else if (bottomItems.get(position * 3).format.equals("2")) {
pic.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
}
} else {
pic.setVisibility(View.GONE);
}
if (bottomItems.size() > position * 3 + 1 && bottomItems.get(position * 3 + 1) != null) {
pic2.setImageBitmap(BitmapFactory.decodeFile(bottomItems.get(position * 3 + 1).getPrev(activity).getAbsolutePath()));
if (bottomItems.get(position * 3 + 1).format.equals("1")) {
pic2.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 2f));
} else if (bottomItems.get(position * 3 + 1).format.equals("2")) {
pic2.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
}
} else {
pic2.setVisibility(View.GONE);
}
if (bottomItems.size() > position * 3 + 2 && bottomItems.get(position * 3 + 2) != null) {
pic3.setImageBitmap(BitmapFactory.decodeFile(bottomItems.get(position * 3 + 2).getPrev(activity).getAbsolutePath()));
if (bottomItems.get(position * 3 + 2).format.equals("1")) {
pic3.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 2f));
} else if (bottomItems.get(position * 3 + 2).format.equals("2")) {
pic3.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
}
} else {
pic3.setVisibility(View.GONE);
}
container.addView(itemView);
return itemView;
}
private void click(JsonParsed.BottomItem bottomItem) {
if (bottomItem.url != null && !bottomItem.url.isEmpty()) {
((MainActivity) activity).showWebView(bottomItem.url);
} else if (bottomItem.video != null && !bottomItem.video.isEmpty()) {
((MainActivity) activity).onPlayMedia(bottomItem.getVideo(activity).getAbsolutePath(), bottomItem.id, false);
}
}
#Override
public void destroyItem(ViewGroup container, int position, Object view) {
container.removeView((LinearLayout) view);
}
Many thanks for any piece of advice
EDIT:
Finally solved the leak.
As Egor pointed out, the main leak has been caused by the Handler,
as it kept the reference onto ViewPager instance so instances were not garbage collected.
Finally I just used lazy solution - WeakHandler (open source library).
I guess it does the same, as described in Egors's article,
but it's pretty handy to use it, as you just using it as usual Handler:
WeakHandler().postDelayed(....) and thats it.
Also I used week references for the Bitmaps and all memory leaks disappeared
Most likely the Handler is the one causing your memory leaks. There's a wonderful article which describes this problem in detail, suggesting that you use a WeakReference to solve the problem.
Is there anyway to allow users to drag-and-drop the navigation tabs in the ActionBar to reorder them on Android 4.0 ICS? I don't mean tabs in a deprecated TabHost, I mean the tabs that you add to the ActionBar that are used in Honeycomb and above.
Thanks!
As far as using ActionBar.Tabs goes, they simply don't have the functionality to achieve this. On the other hand, creating your own custom class that mimics the Tabs is easy enough, then all you'd have to do is create and add an OnDragListener and OnTouchListener for the View you use to fill your tab bar.
For example, this is a class I use in one of my apps that mimics ActionBar.Tabs.
ScrollableTabView
public class ScrollableTabView extends HorizontalScrollView implements OnPageChangeListener {
private final Context mContext;
private final LinearLayout mContainer;
private final ArrayList<View> mTabs = new ArrayList<View>();
private final int mDividerColor = 0xFF636363;
private int mDividerMarginTop = 12;
private int mDividerMarginBottom = 12;
private int mDividerWidth = 1;
private ViewPager mPager;
private TabAdapter mAdapter;
private Drawable mDividerDrawable;
public ScrollableTabView(Context context) {
this(context, null);
}
public ScrollableTabView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ScrollableTabView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
mContext = context;
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
mDividerMarginTop = (int)(getResources().getDisplayMetrics().density * mDividerMarginTop);
mDividerMarginBottom = (int)(getResources().getDisplayMetrics().density * mDividerMarginBottom);
mDividerWidth = (int)(getResources().getDisplayMetrics().density * mDividerWidth);
setHorizontalScrollBarEnabled(false);
setHorizontalFadingEdgeEnabled(false);
mContainer = new LinearLayout(context);
mContainer.setOrientation(LinearLayout.HORIZONTAL);
mContainer.setLayoutParams(params);
addView(mContainer);
}
/**
* Set the tabs Adapter
*
* #param adapter
*/
public void setAdapter(TabAdapter adapter) {
mAdapter = adapter;
if (mPager != null && mAdapter != null) {
initTabs();
}
}
/**
* Attach ViewPager
*
* #param pager
*/
public void setViewPager(ViewPager pager) {
mPager = pager;
mPager.setOnPageChangeListener(this);
if (mPager != null && mAdapter != null) {
initTabs();
}
}
/**
* Initiate the tabs
*/
private void initTabs() {
mContainer.removeAllViews();
mTabs.clear();
if (mAdapter == null) {
return;
}
for (int i = 0; i < mPager.getAdapter().getCount(); i++) {
final int index = i;
final View tab = mAdapter.getView(i);
mContainer.addView(tab);
tab.setFocusable(true);
mTabs.add(tab);
if (i != mPager.getAdapter().getCount() - 1) {
mContainer.addView(getSeparator());
}
tab.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (mPager.getCurrentItem() == index) {
selectTab(index);
} else {
mPager.setCurrentItem(index, true);
}
}
});
}
selectTab(mPager.getCurrentItem());
}
#Override
public void onPageScrollStateChanged(int state) {
// Nothing to do
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Nothing to do
}
#Override
public void onPageSelected(int position) {
selectTab(position);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
selectTab(mPager.getCurrentItem());
}
}
/**
* #return Separates the tabs
*/
private View getSeparator() {
final View v = new View(mContext);
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mDividerWidth,
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
params.setMargins(0, mDividerMarginTop, 0, mDividerMarginBottom);
v.setLayoutParams(params);
if (mDividerDrawable != null) {
v.setBackground(mDividerDrawable);
} else {
v.setBackgroundColor(mDividerColor);
}
return v;
}
/**
* #param position
*/
private void selectTab(int position) {
for (int i = 0, pos = 0; i < mContainer.getChildCount(); i += 2, pos++) {
final View tab = mContainer.getChildAt(i);
tab.setSelected(pos == position);
}
final View selectedTab = mContainer.getChildAt(position * 2);
final int w = selectedTab.getMeasuredWidth();
final int l = selectedTab.getLeft();
final int x = l - this.getWidth() / 2 + w / 2;
smoothScrollTo(x, this.getScrollY());
}
}
TabAdapter
public interface TabAdapter {
public View getView(int position);
}
Attach your TabAdapter
public class ScrollingTabsAdapter implements TabAdapter {
private final FragmentActivity activity;
private final LayoutInflater inflater;
private Button mTabs;
// Tab titles
private static final String[] mTitles = {
"RECENT", "ARTISTS", "ALBUMS", "SONGS", "PLAYLISTS", "GENRES"
};
/**
* #param act
*/
public ScrollingTabsAdapter(FragmentActivity act) {
activity = act;
inflater = activity.getLayoutInflater();
}
#Override
public View getView(int position) {
mTabs = (Button)inflater.inflate(R.layout.tabs, null);
if (position < mTitles.length) {
mTabs.setText(mTitles[position]);
}
return mTabs;
}
}
You can use the default drawables and attributes of the real ActionBar.Tabs to style the Button you inflate. You can grab them from the SDK or probably somewhere on the web. To use it, attach a ViewPager object to the ScrollableTabView and add each of your Fragments in a FragmentPagerAdapter. This is what they look like, if you're curious about the style after adding the default drawables and attributes
As far as dragging and dropping goes, Android has some nice docs on their website. Drag and Drop
There are also some easy to follow tutorials on the web. Android Drag and Drop Tutorial, via Lars Vogel
Or you can always simply use Google to find more