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
Related
I had a simple spinner which i working perfect. Now I wanted to change it that a user can able to search the items in it. By following the below code I have done changes.
Sample code
//Gradle
compile 'com.toptoche.searchablespinner:searchablespinnerlibrary:1.3.1'
//activity_main.xml
<com.toptoche.searchablespinnerlibrary.SearchableSpinner
android:id="#+id/searchable_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
// In main activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SearchableSpinner searchableSpinner = (SearchableSpinner) findViewById(R.id.searchable_spinner);
String[] names = new String[]{"India","CHINA","UK","US","MALYSIA"};
ArrayAdapter arrayAdapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_spinner_dropdown_item,names);
searchableSpinner.setAdapter(arrayAdapter);
searchableSpinner.setTitle("Select Item");
searchableSpinner.setPositiveButton("OK");
}
Output
On click of the dropdown
What i have done?
//added the library in gradle
compile 'com.toptoche.searchablespinner:searchablespinnerlibrary:1.3.1'
//new_form_layout (i have created this)
<com.toptoche.searchablespinnerlibrary.SearchableSpinner
android:id="#+id/smart_msn_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:gravity="right" />
**In My Fragment**
#BindView(R.id.smart_msn_spinner)
SearchableSpinner smartMsnSpinner;
Now I have created a bindListners() function in which I am binding all the values and I am calling it in my onCreateView function
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (view == null) {
view = inflater.inflate(R.layout.new_form_layout, container, false);
ButterKnife.bind(this, view);
bindListners(); // here i am calling it
imsiNo.setVisibility(View.GONE);
setupUI(mScrollView);
}
Bundle arguments = getArguments();
if (arguments != null && arguments.containsKey("install_id")) {
isNewInstall = false;
editInstallId = arguments.getString("install_id");
getActivity().setTitle(getString(R.string.title_fragment_edit_form));
setEditData();
imsiNo.setVisibility(View.GONE);
resetFormButton.setVisibility(View.GONE);
} else {
getActivity().setTitle(getString(R.string.title_fragment_new_form));
}
/*mCoordinatesReceiver = new CoordinatesReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Common.GetCoordinatesAction());
getActivity().registerReceiver(mCoordinatesReceiver, intentFilter);*/
return view;
}
bindListeners(){
.......
//Searchable smartMsnSpinner spinner and adapter
meterSrArrayList = new ArrayList<String>();
meterSrNumAdapter = new ArrayAdapter<String>(getActivity(), R.layout.custom_spinner_layout, meterSrArrayList);
smartMsnSpinner.setAdapter(meterSrNumAdapter);
smartMsnSpinner.setTitle("Select Item");
smartMsnSpinner.setPositiveButton("Ok");
smartMsnSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
selectedMeterNo = meterSrArrayList.get(position);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
On running my app i am just getting simple drop down list as before.
I don't know what is the problem and what I am missing as i have done everything that is in the sample.
I have run the sample code in my device and it's working fine. I don't know why it's not working on my app
Update
After watching the logcatthe error i am seeing is
Parcelable encountered IOException writing serializable object (name = com.toptoche.searchablespinnerlibrary.SearchableSpinner)
Any help would be highly appreciated.
I'm using same library but I didn't have this problem.
Anyway I had others problem both on visualization, Parcelization and Re-Initializing the same view (the scroll down view) with this library.
Personally I extended the "SearchableSpinner" and made this changes:
public class BaseSearchableSpinner extends SearchableSpinner
implements SearchView.OnAttachStateChangeListener {
private static final String TAG = BaseSearchableSpinner.class.getSimpleName();
private static final String TAG_DIALOG = TAG.concat(".dialog");
// Dialogs Tags
private static final String TAG_DIALOG_SEARCHABLE_LIST = TAG_DIALOG.concat(".searchableList");
// SearchableSpinner Fields
private static final String FIELD_SEARCHABLE_LIST_DIALOG = "_searchableListDialog";
private static final String FIELD_SEARCH_VIEW = "_searchView";
private static final String FIELD_SEARCHABLE_ITEM = "_searchableItem";
private static final String FIELD_ARRAY_ADAPTER = "_arrayAdapter";
private static final String FIELD_ITEMS = "_items";
private boolean mIsListDialogAdded;
private boolean mIsListenerAdded;
public BaseSearchableSpinner(Context context) {
super(context);
initListDialog();
}
public BaseSearchableSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
initListDialog();
}
public BaseSearchableSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initListDialog();
}
/** Override SearchableSpinner Methods **/
#Override
public boolean onTouch(View v, MotionEvent event) {
try{
SearchableListDialog sld = (SearchableListDialog) FieldUtils.readField(this, FIELD_SEARCHABLE_LIST_DIALOG, true);
ArrayAdapter adapter = (ArrayAdapter) FieldUtils.readField(this, FIELD_ARRAY_ADAPTER, true);
List items = (List) FieldUtils.readField(this, FIELD_ITEMS, true);
if (sld != null && adapter != null && items != null && event.getAction() == MotionEvent.ACTION_UP && checkIfListDialogNotAdded()) {
if(mIsListenerAdded){
mIsListDialogAdded = true;
}
items.clear();
for (int i = 0x0; i < adapter.getCount(); i++) {
items.add(adapter.getItem(i));
}
sld.show(scanForActivity(getContext()).getFragmentManager(), TAG_DIALOG_SEARCHABLE_LIST);
}
} catch (IllegalAccessException iaE){
EMaxLogger.onException(TAG, iaE);
}
return true;
}
/** Override SearchView.OnAttachStateChangeListener Methods **/
#Override
public void onViewAttachedToWindow(View view) {
mIsListDialogAdded = true;
}
#Override
public void onViewDetachedFromWindow(View view) {
mIsListDialogAdded = false;
}
/** Private Methods **/
private void initListDialog(){
try{
SearchableListDialog oldD = (SearchableListDialog) FieldUtils.readField(this, FIELD_SEARCHABLE_LIST_DIALOG, true);
if(oldD != null) {
BaseSearchableListDialog newD = new BaseSearchableListDialog(this);
newD.setArguments(oldD.getArguments());
newD.setOnSearchableItemClickListener(this);
FieldUtils.writeField(this, FIELD_SEARCHABLE_LIST_DIALOG, newD, true);
}
} catch (IllegalAccessException iaE){
EMaxLogger.onException(TAG, iaE);
}
}
private void initListenerOnCloseSearchView(SearchableListDialog instance) {
try{
SearchView sv = (SearchView) FieldUtils.readField(instance, FIELD_SEARCH_VIEW, true);
if(sv != null){
sv.addOnAttachStateChangeListener(this);
mIsListenerAdded = true;
}
} catch (IllegalAccessException iaE){
EMaxLogger.onException(TAG, iaE);
}
}
private boolean checkIfListDialogNotAdded(){
return !mIsListDialogAdded && scanForActivity(getContext()).getFragmentManager().findFragmentByTag(TAG_DIALOG_SEARCHABLE_LIST) == null;
}
private Activity scanForActivity(Context cont) {
if (cont == null)
return null;
else if (cont instanceof Activity)
return (Activity) cont;
else if (cont instanceof ContextWrapper)
return scanForActivity(((ContextWrapper) cont).getBaseContext());
return null;
}
/** Private Classes **/
#SuppressLint("ValidFragment")
public static class BaseSearchableListDialog extends SearchableListDialog {
private BaseSearchableSpinner mOuter;
private BaseSearchableListDialog(BaseSearchableSpinner bss){
super();
mOuter = bss;
}
/** Override SearchableListDialog Methods **/
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog dialog = (AlertDialog) super.onCreateDialog(savedInstanceState);
mOuter.initListenerOnCloseSearchView(this);
return dialog;
}
}
}
Try using this and see if it works.
Also I changed the adapter to have custom texts and don't always display the "toString" of an object :/ I normally use the toString only for debugging purposes, so to show info about an object I make specific methods.
So this is the class for the Adapter:
public abstract class BaseSearchableSpinnerAdapter<T> extends ArrayAdapter<CharSequence> {
// Empty Item Label
protected static final String LABEL_EMPTY_ITEM = " ";
// Label Length
protected static final int LABEL_LENGTH = 50;
// Spinner Adapter Positions
public static final int POS_ITEM_NOT_FOUND = -0x1;
public static final int POS_EMPTY_ITEM = 0x0; // Not always true, depends if implemented
protected List<T> mItems;
private int mResLayout;
public BaseSearchableSpinnerAdapter(#NonNull Context context, #LayoutRes int resource) {
super(context, resource);
mItems = new ArrayList<>();
mResLayout = resource;
}
/** Abstract Methods **/
public abstract <T extends CharSequence> T getLabelView(int pos);
/** Override ArrayAdapter Methods **/
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(mResLayout, parent, false);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.spinner_default, parent, false);
}
}
TextView tv = convertView.findViewById(R.id.text1);
if(tv != null){
tv.setText(getLabelView(position));
}
return convertView;
}
#Override
public void clear(){
mItems.clear();
super.clear();
}
/** Public Methods **/
public void addAll(List<T> objs){
clear();
ArrayList<CharSequence> labels = new ArrayList<>();
if(objs != null && objs.size() > 0x0){
mItems.addAll(objs);
for(int i = 0x0; i < objs.size(); i++){
labels.add(getLabelView(i));
}
}
super.addAll(labels);
}
public T getMyItem(int pos){
if(mItems != null && mItems.size() > pos && pos != -0x1){
return mItems.get(pos);
}
return null;
}
public List<T> getMyItems(){
return mItems;
}
}
Extend this class and use the object you want.
LABEL_EMTPY_ITEM is a long series of spaces because in some app when the text don't take all the line in the list view it will be clickable only on the text and not on all the line.. So when you have no item the clikable part of the line is a little small piece on the left (in my case, I had this problem).
Example to extend this base Adapter class:
public class MyObjectSearchableSpinnerAdapter extends BaseSearchableSpinnerAdapter<MyObject> {
private #StringRes int mIdFstr;
public MyObjectSearchableSpinnerAdapter(#NonNull Context context, #LayoutRes int resource){
this(context, resource, R.string.fstr_two_fields_dash);
}
public MyObjectSearchableSpinnerAdapter(#NonNull Context context, #LayoutRes int resource, int idFstr){
super(context, resource);
mIdFstr = idFstr;
}
/** Override BaseSearchableSpinnerAdapter Methods **/
#Override
public <T extends CharSequence> T getLabelView(int pos) {
MyObject item = mItems.get(pos);
if(item != null){
return (T) (!TextUtils.isEmpty(item.getName2()) ?
getContext().getString(mIdFstr, item.getName1(), item.getName2()) :
item.getName1());
}
return (T) LABEL_EMPTY_ITEM;
}
/** Public Methods **/
public int getItemPosition(int idMyObject){
return getItemPosition(String.valueOf(idMyObject));
}
public int getItemPosition(String idMyObject){
if(mItems != null && mItems.size() > 0x0){
for(int i = 0x0; i < mItems.size(); i++){
MyObject item = mItems.get(i);
if(item != null && idMyObject.equals(item.getId())){
return i;
}
}
}
return POS_ITEM_NOT_FOUND;
}
}
Example Init BaseSearchableSpinner:
private void initBaseSearchableSpinnerMyObjects(){
MyObjectSearchableSpinnerAdapter adapter = new MyObjectSearchableSpinnerAdapter(getContext(), R.layout.spinner_default);
adapter.setDropDownViewResource(R.layout.spinner_default);
mBaseSearchableSpinnerMyObjects.setAdapter(adapter);
}
Example Add your list of MyObject to the adapter:
((MyObjectSearchableSpinnerAdapter)mBaseSearchableSpinnerMyObjects.getAdapter()).addAll(items);
Example Get back an object from a BaseSearchableSpinner with an extensions of BaseSearchableAdapter with a list of MyObject :
MyObject obj = ((MyObjectSearchableSpinnerAdapter) mBaseSearchableSpinnerMyObjects.getAdapter()).getMyItem(mBaseSearchableSpinnerMyObjects.getSelectedItemPosition());
Have a nice coding and day!
bb
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);
}
I'm actually trying to display the line numbering at the left of an EditText as shown in the figure.
The problem is that whenever a new line is added or removed to the file, it take a while to recalculate the string containing the line numbering.
What I do is the following: whenvere there is a type, I check whether the line count of the EditText has changed, if it is the case I just append the missing line at the end of the TextView showing the line numbering.
So if we take the piece of code in the image and press the new line key, my code get the line count (14 since we added a line) and append 14 + System.lineSeparator() to the TextView by using linesTextView.append(newText).
As said the application is lagging. Is there a way to efficently modify the content of a TextView?
Another solution that came in my mind was to use a TextView for each line number and just remove or add a new TextView when the line count of the content has changed. The problem about that is that I cannot make the TextViews be aligned with the lines of the EditText.
Thank you
EDIT:
Forgot to add the code
public class CodeEditView extends LinearLayout implements TouchEditText.TouchEditTextListener{
private View root;
private InternalFile file;
private TouchEditText code;
private ScrollView scroll;
private int linesCount;
private TextView lines;
private int currentStartLine;
private int currentEndLine;
public CodeEditView(Context context) {
super(context);
init(null, context);
}
public CodeEditView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, context);
}
private void init(AttributeSet attrs, Context context) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
root = layoutInflater.inflate(R.layout.edit_code_layout, this).findViewById(R.id.scroll_view);
this.lines = (TextView) findViewById(R.id.edit_code_lines_view);
this.code = (TouchEditText) findViewById(R.id.edit_code_content_view);
code.setRoot(root);
code.setListener(this);
linesCount = 0;
scroll = (ScrollView) findViewById(R.id.scroll_view);
scroll.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
// int scrollY = scroll.getScrollY();
// if(scrollY != 0) {
// code.scrollEvent();
// if(currentStartLine < code.getCurrentStartLine()) {
//
// String c = lines.getText().toString();
// String cc = c.replaceFirst("\\d+", " ");
// cc += Integer.toString(code.getCurrentEndLine()+10) + System.lineSeparator();
// lines.setText(cc);
// currentStartLine = code.getCurrentStartLine();
//
// } else {
//// CharSequence c = lines.getText();
//// lines.setText(lines.getText().);
// }
// }
int scrollY = scroll.getScrollY();
if(scrollY != 0) {
code.scrollEvent();
}
}
});
}
public void setFile(InternalFile file) {
this.file = file;
}
public void setLexer(Lexer lexer) {
code.setLexer(lexer);
}
private void initLines() {
// currentStartLine = code.getCurrentStartLine();
// currentEndLine = code.getCurrentEndLine();
// int usedLines = code.getLineCount();
// if(usedLines != linesCount) {
//
// String text = "";
//
// for(int i = currentStartLine; i <= currentEndLine+10; i++) {
// text += Integer.toString(i) + System.lineSeparator();
// }
// lines.setText(text);
// linesCount = usedLines;
// }
int usedLines = code.getLineCount();
String text = "";
for(int i = 1; i <= usedLines; i++) {
text += Integer.toString(i) + System.lineSeparator();
}
Log.d("TEXT", Integer.toString(usedLines));
lines.setText(text);
linesCount = usedLines;
}
public void initText(String content) {
code.initText(content);
code.post(new Runnable() {
#Override
public void run() {
initLines();
}
});
}
public void setFont(Typeface typeFace) {
this.lines.setTypeface(typeFace);
this.code.setTypeface(typeFace);
}
#Override
public void onTyped(String text) {
EventBus.getDefault().post(new UpdateCacheFileEvent(text,file));
setLines();
}
public void forceSyntax(Syntax s) {
code.forceSyntax(s);
}
private void setLines() {
int usedLines = code.getLineCount();
if(usedLines > linesCount) {
lines.append(Integer.toString(usedLines) + System.lineSeparator());
linesCount = usedLines;
} else {
}
}
}
I want my Android application to work with different skins. So what I plan is changing all the images and fonts I used in the application for different skins.
What is the best way for this? How can i init all imageviews with proper image resources?
P.S : I want to change the skin at runtime and don't want to restart the app.
Thanks..
you can create a ThemeManager class where you can manage the themes
public class ThemeManager {
static ThemeManager tmanage;
int ThemeCode;
int topBarBackground, bottomBarBackground, buttonBackground, textColor;
ArrayList<ThemeWrapper> twrapper = new ArrayList<ThemeWrapper>();
public static ThemeManager getInstance() {
if (tmanage != null)
return tmanage;
else
tmanage = new ThemeManager();
return tmanage;
}
public void setThemeCode(int themeCode) {
ThemeCode = themeCode;
setBottomBarBackground(themeCode);
setTopBarBackground(themeCode);
setButtonBackground(themeCode);
setTextColor(themeCode);
}
#SuppressWarnings("deprecation")
public void setTheme(ArrayList<View> v) {
for (int i = 0; i < v.size(); i++) {
if (v.get(i) instanceof RelativeLayout) {
RelativeLayout layout = (RelativeLayout) v.get(i);
layout.setBackgroundColor(getTopBarBackground());
}
else if (v.get(i) instanceof Button) {
Button layout = (Button) v.get(i);
layout.setBackgroundDrawable(getShape(getButtonBackground()));
layout.setPadding(5, 5, 5, 5);
layout.setTextSize(12);
}
else if (v.get(i) instanceof LinearLayout) {
LinearLayout layout = (LinearLayout) v.get(i);
layout.setBackgroundColor(getBottomBarBackground());
}
else if (v.get(i) instanceof TextView) {
TextView layout = (TextView) v.get(i);
layout.setTextColor(getTextColor());
}
}
}
private Drawable getShape(int col){
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR, new int[] { col,
col, col});
gradientDrawable.setGradientType(GradientDrawable.RECTANGLE);
gradientDrawable.setCornerRadii(getRandomFloatArray());
gradientDrawable.setGradientCenter(0.0f, 0.45f);
gradientDrawable.setStroke(1, Color.WHITE);
gradientDrawable.setSize(60, 30);
return gradientDrawable;
}
private float [] getRandomFloatArray(){
float[] floats = new float[8];
for (int i =0; i < floats.length; i++){
floats[i] = 2;
}
return floats;
}
public int getTopBarBackground() {
return topBarBackground;
}
public void setTopBarBackground(int topBarBackground) {
this.topBarBackground = topBarBackground;
}
public int getBottomBarBackground() {
return bottomBarBackground;
}
public void setBottomBarBackground(int bottomBarBackground) {
this.bottomBarBackground = bottomBarBackground;
}
public int getButtonBackground() {
return buttonBackground;
}
public void setButtonBackground(int buttonBackground) {
this.buttonBackground = buttonBackground;
}
public int getTextColor() {
return textColor;
}
public void setTextColor(int textColor) {
this.textColor = textColor;
}
}
I was changing the colors for the views you can set the drawables as well now use this in your activities onResume function
private void changeColor() {
// TODO Auto-generated method stub
ArrayList<View> v = new ArrayList<View>();
v.add(topBar);
v.add(bottomBar);
v.add(updateBio);
v.add(updateEvents);
v.add(updateGallery);
v.add(updatePersonalStyle);
v.add(updateVip);
v.add(updateStatus);
v.add(NameLink);
v.add(changeProfilePicture);
ThemeManager theme = ThemeManager.getInstance();
theme.setThemeCode(PreferenceManager.getDefaultSharedPreferences(
getApplicationContext()).getInt("Theme_Color",
Color.parseColor("#7606a5")));
theme.setTheme(v);
}
just call this function in the onResume
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