Drag-and-drop action bar tabs (Android 4.0 ICS)? - android

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

Related

Is it possible to create only one view swipeable in viewpager android

I am using viewpager in my application and i want to make only one view Swipeable . Is it possible to do so.
I don't want the buttons at the bottom move when i swipe i only want the textview to change.
Any way can anyone help i searched a lot but couldn't find anything.
Here is my code.
There is a lot of code i can't paste all of it here.
Here is the link to the rest of it.
ViewPagerAdapter.java
public class ViewPagerAdapter extends PagerAdapter {
private Activity activity;
private Cursor cursor;
private int i = 0;
private int f = 0;
OnItemClickListener mOnItemClickListener;
public ViewPagerAdapter(Activity activity, Cursor cursor) {
this.activity = activity;
this.cursor = cursor;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
#Override
public int getCount() {
return cursor.getCount();
}
#Override
public boolean isViewFromObject(#NonNull View view, #NonNull Object object) {
return view == object;
}
#NonNull
#Override
public Object instantiateItem(#NonNull final ViewGroup container, final int position) {
final Context context = container.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.quote_detail_viewpager, container, false);
final TextView tvQuotes;
final ImageButton favouriteBtn, shareBtn, copyBtn, bgBtn, fontBtn;
tvQuotes = v.findViewById(R.id.vp_quotes);
favouriteBtn = v.findViewById(R.id.vp_fav);
shareBtn = v.findViewById(R.id.vp_share);
copyBtn = v.findViewById(R.id.vp_copy);
bgBtn = v.findViewById(R.id.vp_bg);
fontBtn = v.findViewById(R.id.vp_font);
int[] fonts = {
R.font.architects_daughter, R.font.artifika, R.font.carter_one,
R.font.expletus_sans, R.font.fredoka_one, R.font.graduate, R.font.jose,
R.font.magnolia, R.font.oswald, R.font.quicksand_bold, R.font.righteous,
R.font.salsa, R.font.schoolbell, R.font.sofadi_one
};
cursor.moveToPosition(position);
String id = cursor.getString(0);
String quote = cursor.getString(1);
String author = cursor.getString(2);
String text = quote + "\n \n - "+ author;
if (new DatabaseHelper(context).isFavourite(id)) {
favouriteBtn.setImageResource(R.drawable.ic_favorite);
}
tvQuotes.setText(text);
tvQuotes.setTypeface(PrefUtils.getTypefaceFromPrefs(context));
favouriteBtn.setOnClickListener(view ->
UtilsHelper.addOrDeleteFavourite(context, favouriteBtn, id, quote, author));
shareBtn.setOnClickListener(view -> UtilsHelper.shareTextIntent(context, text));
copyBtn.setOnClickListener(view -> UtilsHelper.copyText(context, text));
bgBtn.setOnClickListener(view -> mOnItemClickListener.onItemClick(view));
fontBtn.setOnClickListener(view -> {
int j = f++;
if (j < fonts.length) {
PrefUtils.setTypefaceFromPrefs(context, fonts[j]);
tvQuotes.setTypeface(PrefUtils.getTypefaceFromPrefs(context));
notifyDataSetChanged();
}
if (f == fonts.length) {
f = 0;
}
});
container.addView(v);
return v;
}
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_NONE;
}
#Override
public void destroyItem(ViewGroup container, int position, #NonNull Object object) {
container.removeView((View) object);
}
public interface OnItemClickListener {
void onItemClick(View view);
}
}
DetailActivity.java
public class QuoteDetailActivity extends AppCompatActivity {
// Max swipe ad interval
private final int MAX_AD_COUNT = 10;
// count swipes
private int SWIPE_COUNT = 0;
// Min swipe ad interval
private int MIN_AD_COUNT = 6;
private int i = 0;
private InterstitialAd mInterstitialAd;
ViewPagerAdapter pagerAdapter;
RelativeLayout rootLayout;
int[] backgrounds;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
Objects.requireNonNull(getSupportActionBar()).hide(); //hide the title bar
setContentView(R.layout.activity_quote_detail);
showInterstitialAd();
// String extras
String incomingActivity = getIntent().getStringExtra(Constants.STRING_EXTRA_INCOMING_ACTIVITY);
int position = getIntent().getIntExtra(Constants.STRING_EXTRA_ADAPTER_POSITION, 0);
DatabaseHelper mDBHelper = new DatabaseHelper(this);
ViewPager viewPager = findViewById(R.id.single_quote_viewpager);
backgrounds = BackgroundUtils.getAllBackgrounds();
rootLayout = findViewById(R.id.root_container);
rootLayout.setBackgroundResource(BackgroundUtils.getBackground(position));
FrameLayout adContainerView = findViewById(R.id.ad_view_container);
adContainerView.post(() ->
AdHelper.loadBanner(this, adContainerView, viewPager));
assert incomingActivity != null;
if (incomingActivity.contains(Constants.ACTIVITY_QUOTES)) {
pagerAdapter = new ViewPagerAdapter(this, mDBHelper.getAllQuotes());
} else if (incomingActivity.contains(Constants.ACTIVITY_FAVOURITE)) {
pagerAdapter = new ViewPagerAdapter(this, mDBHelper.getFavourites());
}
viewPager.setAdapter(pagerAdapter);
viewPager.setCurrentItem(position); // Setup recycleView Item position
pagerAdapter.setOnItemClickListener(new ViewPagerAdapter.OnItemClickListener() {
#Override
public void onItemClick(View view) {
changeBackground();
}
});
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
int count = SWIPE_COUNT++;
if (count >= MIN_AD_COUNT) {
if (mInterstitialAd != null) {
mInterstitialAd.show(QuoteDetailActivity.this);
SWIPE_COUNT = 0;
}
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void changeBackground() {
int j = i++;
if (j < backgrounds.length) {
rootLayout.setBackgroundResource(backgrounds[j]);
}
if (i == backgrounds.length) {
i = 0;
}
}
private void showInterstitialAd() {
MobileAds.initialize(this, initializationStatus -> {
});
AdRequest adRequest = new AdRequest.Builder().build();
InterstitialAd.load(this, getString(R.string.interstitial_ad_unit),
adRequest, new InterstitialAdLoadCallback() {
#Override
public void onAdLoaded(#NonNull InterstitialAd interstitialAd) {
// The mInterstitialAd reference will be null until
// an ad is loaded.
mInterstitialAd = interstitialAd;
mInterstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() {
#Override
public void onAdDismissedFullScreenContent() {
// Called when fullscreen content is dismissed.
if (MIN_AD_COUNT < MAX_AD_COUNT) {
MIN_AD_COUNT++;
}
}
#Override
public void onAdShowedFullScreenContent() {
// Called when fullscreen content is shown.
// Make sure to set your reference to null so you don't
// show it a second time.
mInterstitialAd = null;
}
});
}
#Override
public void onAdFailedToLoad(#NonNull LoadAdError loadAdError) {
// Handle the error
mInterstitialAd = null;
}
});
}
}
Sure, something like the below pseudo_xml
<Parent View> -- background set to image
<ViewPager> -- only this part is swipable
<View Containing Buttons Fixed To The Bottom Of Parent/>
</Parent View>

How do I loop through 3 card fragments in a grid pager adapter?

I am trying to loop through 3 card fragments in grid pager adapter. My grid pager adapter code is here:
/**
* Constructs fragments as requested by the GridViewPager. For each row a different background is
* provided.
* <p>
* Always avoid loading resources from the main thread. In this sample,the background images are
* loaded from an background task and then updated using {#link #notifyRowBackgroundChanged(int)}
* and {#link #notifyPageBackgroundChanged(int, int)}.
*/
public class SampleGridPagerAdapter extends FragmentGridPagerAdapter {
private static final int TRANSITION_DURATION_MILLIS = 100;
private final Context mContext;
private List<Row> mRows;
private ColorDrawable mDefaultBg;
private ColorDrawable mClearBg;
#SuppressLint("ResourceAsColor")
public SampleGridPagerAdapter(Context ctx, FragmentManager fm) {
super(fm);
mContext = ctx;
mRows = new ArrayList<Row>();
mRows.add(new Row(cardFragment(R.string.welcome_title, R.string.welcome_text),
cardFragment(R.string.about_title, R.string.about_text),
cardFragment(R.string.cards_title, R.string.cards_text),
cardFragment(R.string.expansion_title, R.string.expansion_text)));
mDefaultBg = new ColorDrawable(R.color.dark_grey);
mClearBg = new ColorDrawable(android.R.color.transparent);
}
LruCache<Integer, Drawable> mRowBackgrounds = new LruCache<Integer, Drawable>(3) {
#Override
protected Drawable create(final Integer row) {
int resid = BG_IMAGES[row % BG_IMAGES.length];
new DrawableLoadingTask(mContext) {
#Override
protected void onPostExecute(Drawable result) {
TransitionDrawable background = new TransitionDrawable(new Drawable[] {
mDefaultBg,
result
});
mRowBackgrounds.put(row, background);
notifyRowBackgroundChanged(row);
background.startTransition(TRANSITION_DURATION_MILLIS);
}
}.execute(resid);
return mDefaultBg;
}
};
LruCache<Point, Drawable> mPageBackgrounds = new LruCache<Point, Drawable>(3) {
#Override
protected Drawable create(final Point page) {
// place bugdroid as the background at row 2, column 1
if (page.y == 2 && page.x == 1) {
int resid = R.drawable.bugdroid_large;
new DrawableLoadingTask(mContext) {
#Override
protected void onPostExecute(Drawable result) {
TransitionDrawable background = new TransitionDrawable(new Drawable[] {
mClearBg,
result
});
mPageBackgrounds.put(page, background);
notifyPageBackgroundChanged(page.y, page.x);
background.startTransition(TRANSITION_DURATION_MILLIS);
}
}.execute(resid);
}
return GridPagerAdapter.BACKGROUND_NONE;
}
};
private Fragment cardFragment(int titleRes, int textRes) {
Resources res = mContext.getResources();
CardFragment fragment =
CardFragment.create(res.getText(titleRes), res.getText(textRes));
// Add some extra bottom margin to leave room for the page indicator
fragment.setCardMarginBottom(
res.getDimensionPixelSize(R.dimen.card_margin_bottom));
return fragment;
}
static final int[] BG_IMAGES = new int[] {
R.drawable.debug_background_1,
R.drawable.debug_background_2,
R.drawable.debug_background_3,
R.drawable.debug_background_4,
R.drawable.debug_background_5
};
/** A convenient container for a row of fragments. */
private class Row {
final List<Fragment> columns = new ArrayList<Fragment>();
public Row(Fragment... fragments) {
for (Fragment f : fragments) {
add(f);
}
}
public void add(Fragment f) {
columns.add(f);
}
Fragment getColumn(int i) {
return columns.get(i);
}
public int getColumnCount() {
return columns.size();
}
}
#Override
public Fragment getFragment(int row, int col) {
Row adapterRow = mRows.get(row);
return adapterRow.getColumn(col);
}
#Override
public Drawable getBackgroundForRow(final int row) {
return mRowBackgrounds.get(row);
}
#Override
public Drawable getBackgroundForPage(final int row, final int column) {
return mPageBackgrounds.get(new Point(column, row));
}
#Override
public int getRowCount() {
return mRows.size();
}
#Override
public int getColumnCount(int rowNum) {
return mRows.get(rowNum).getColumnCount();
}
class DrawableLoadingTask extends AsyncTask<Integer, Void, Drawable> {
private static final String TAG = "Loader";
private Context context;
DrawableLoadingTask(Context context) {
this.context = context;
}
#Override
protected Drawable doInBackground(Integer... params) {
Log.d(TAG, "Loading asset 0x" + Integer.toHexString(params[0]));
return context.getResources().getDrawable(params[0]);
}
}
}
I started from the google developer example code found here : https://developer.android.com/samples/GridViewPager/project.html and modified it to be one row of card fragments. Is there any way to repeatedly loop through them?

Searchable Spinner not working in android

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

Memory Leak due to PopupWindow

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);
}

synchronization of listviews inside view pager

Hi i got the viewpager :contains one fragment with two different views inside [A] and [B]. both views contain a list view. when I scroll , list view [A] and switch to second fragment I want list view [B] also be scrolled with the same amount. I've searched that I can set onTouchListener to the listview.please find below the code i used.
viewpager adapter
public class ViewPagerAdapter extends FragmentStatePagerAdapter implements
CurrencyScrollListener {
private String nBankName;
private List<Currency> list;
private String mycode;
private CurrencyViewPager[] mFragments = new CurrencyViewPager[2];
private int mCurrentScrollPosition;
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return super.getItemPosition(object);
}
public CurrencyViewPager getCurrencyFragment(int index) {
CurrencyViewPager pager = mFragments[index];
CurrencyViewPager other = mFragments[1-index];
if (pager == null) {
if (index == 0) {
pager = new CurrencyViewPager(list, nBankName, true, mycode,
this, 0);
} else {
pager = new CurrencyViewPager(list, nBankName, false, mycode,
this, 1);
}
mFragments[index] = pager;
}
if(other!=null && other.getCurrentPosition() != pager.getCurrentPosition()) {
pager.scrollTo( other.getCurrentPosition() );
}
return pager;
}
public ViewPagerAdapter(FragmentManager fm, List<Currency> currencyList,
String name, String code) {
super(fm);
this.list = currencyList;
this.nBankName = name;
this.mycode = code;
}
#Override
public Fragment getItem(int item) {
Log.i("ViewPagerAdapter","Currency Fragment #"+item);
return getCurrencyFragment(item);
}
#Override
public int getCount() {
return 2;
}
}
}
And single fragment containing list view
public class CurrencyViewPager extends BaseFragment {
private ListView mcurrencyListview;
private GeoCurrencyService mCurrency = GeoCurrencyService.getInstance();
ButtonPressListener buttonListener;
private CurrencyAdapter mCurrencyAdapter;
private String bankName;
private boolean checkFlag;
private ProgressBar mCurrencyProgress;
private TextView mCurrencyLoading;
private List<Currency> mList;
private String recCode;
private static int Firstposition;
private final CurrencyScrollListener listener;
private final int mListViewIndex;
// constructor
public CurrencyViewPager(List<Currency> myList, String name, boolean flag,String code, CurrencyScrollListener listener, int listViewIndex) {
super();
this.mList = myList;
this.bankName = name;
this.checkFlag = flag;
this.recCode = code;
this.listener = listener;
this.mListViewIndex = listViewIndex;
}
// interface
public interface ButtonPressListener {
public void onListItemPressed(Currency object, String name,String code);
}
// attach on listener
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
buttonListener = (ButtonPressListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement onButtonPressed");
}
}
// creating the main View.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.listbuy, container, false);
mcurrencyListview = (ListView) view.findViewById(R.id.BuyList);
mCurrencyAdapter = new CurrencyAdapter(getActivity(),
R.layout.currency_row, mList, checkFlag, getLanguage());
mcurrencyListview.setAdapter(mCurrencyAdapter);
// calling method getCurrency which take bank code as a parameter
/*getCurrency(code,null);*/
// reference to the list view of the corresponding layout
mcurrencyListview.setOnItemClickListener(new OnItemClickListener() {
// onClick on the List item
#Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
// getting Currency object according to the position
Currency currencyObject = mCurrencyAdapter.getItem(position);
// sending the object to the mainActivity
buttonListener.onListItemPressed(currencyObject, bankName,recCode);
}
});
return view;
}
}
First off, you should be using FragmentPagerAdapter instead of FragmentStatePagerAdapter (Look here for more information).
Now, to the question at hand.
Use the following to get the current position of ListView [A]
int index = mListA.getFirstVisiblePosition();
View v = mListA.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
And use this to set the position of ListView [B]:
mListB.setSelectionFromTop(index, top);
Look at this SO post for more information.

Categories

Resources