disable the scroll effect of the action list - android

I've got a GuidedStepSupportFragment fragment like this.
public class SampleStepFragment extends GuidedStepSupportFragment {
#NonNull
#Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = "Title";
String breadcrumb = "Breadcrumb";
String description = "Description";
Drawable icon = getActivity().getDrawable(R.drawable.ic_videocam_black_24dp);
return new GuidanceStylist.Guidance(title, description, breadcrumb, icon);
}
#Override
public void onCreateActions(#NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
addAction(actions, ACTION_CONTINUE, "Action1");
addAction(actions, ACTION_BACK, "Action2");
}
}
Problem: When I scroll the action list, it shows like this;
But I want to something like this;
How can I disable this effect on my action list?
Thanks

I managed it and it wasn't easy to figure out.
There's no supported way of doing it, since the APIs that actually make this possible are package private or hidden from public use on purpose. (You can do it yourself, but you just end up copying classes from the leanback libraries.)
The solution:
#Override
public void onCreateActions(#NonNull List<GuidedAction> actions, Bundle savedInstanceState) {
addAction(actions, GuidedAction.ACTION_ID_CONTINUE, "Action1");
addAction(actions, GuidedAction.ACTION_ID_CANCEL, "Action2");
// Run code delayed on mainThread (any other/better method can/should be used)
// It's delayed because if focus scroll is disabled, the list will stick to the top of the layout
new Handler(Looper.getMainLooper()).postDelayed(this::disableFocusScroll, 500);
}
private void disableFocusScroll() {
RecyclerView.LayoutManager layoutManager = SampleStepFragment.this.getGuidedActionsStylist().getActionsGridView().getLayoutManager();
try {
Method method = layoutManager.getClass().getMethod("setFocusScrollStrategy", int.class);
method.invoke(layoutManager, 1 /* FOCUS_SCROLL_ITEM */);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "disableFocusScroll: ", e);
}
}
Full example
The explanation:
A GuidedStepSupportFragment requests a GuidedActionsStylist which is responsible for rendering the list items on the right side. source
The GuidedActionsStylist stylist inflates the layout lb_guidedactions.xml which contains a VerticalGridView source
The VerticalGridView extends BaseGridView and creates a GridLayoutManager as its layout manager. This GridLayoutManager is sadly package private and final... (android why..?). It has the method setFocusScrollStrategy which is used to determine how scrolling behaves.source
See the different focus scroll strategies:
/**
* Always keep focused item at a aligned position. Developer can use
* WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
* In this mode, the last focused position will be remembered and restored when focus
* is back to the view.
* #hide
*/
#RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_ALIGNED = 0;
/**
* Scroll to make the focused item inside client area.
* #hide
*/
#RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_ITEM = 1;
/**
* Scroll a page of items when focusing to item outside the client area.
* The page size matches the client area size of RecyclerView.
* #hide
*/
#RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_PAGE = 2;
So since the API is hidden, we just use reflection to expose the setFocusScrollStrategy method and set it to FOCUS_SCROLL_ITEM.
We can't do this immediately though, since without the default scroll setting, the list items will pop to the top of the layout and won't stay centered. So I added a delay of 500ms which is horrible... If you manage to find out when it's best to trigger this, let me know.

It turns out there is a much more elegant and simpler solution. It is enough to add to the body of onViewCreated in the class that inherits guideStepSupportFragment windowAlignment option (there are other alignment options):
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// ...
val gridView = guidedActionsStylist.actionsGridView
gridView.windowAlignment = VerticalGridView.WINDOW_ALIGN_BOTH_EDGE
// ...
super.onViewCreated(view, savedInstanceState)
}

Here is a simple way to disable VerticalGridView scrolling
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
VerticalGridView gridView = getGuidedActionsStylist().getActionsGridView();
gridView.setScrollEnabled(false);
super.onViewCreated(view, savedInstanceState);
}

Related

RecyclerView onBindViewHolder called only once inside Tab layout

I've four tabs and four fragments (each one for each tab).
Each fragment has a vertical recycler view. Since all fragments view look similar I'm re-using the same layout file, same recycler view items and same adapter.
The issue is that only one item is loaded under the first tab and third tab and fourth tab, While the second tab successfully loads the entire data.
I hope image added below gives better understanding regarding the issue.
Here is my adapter code
public class OthersAdapter extends RecyclerView.Adapter<OthersAdapter.OthersViewHolder> {
private final Context context;
private final ArrayList<LocalDealsDataFields> othersDataArray;
private LayoutInflater layoutInflater;
public OthersAdapter(Context context, ArrayList<LocalDealsDataFields> othersDataArray) {
this.context = context;
this.othersDataArray = othersDataArray;
if (this.context != null) {
layoutInflater = LayoutInflater.from(this.context);
}
}
class OthersViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView othersSmallTitleTextView;
ImageView othersImageView;
OthersViewHolder(View itemView) {
super(itemView);
othersSmallTitleTextView = (TextView) itemView.findViewById(R.id.others_small_title);
othersImageView = (ImageView) itemView.findViewById(R.id.others_image);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Intent couponDetailsItem = new Intent(context, LocalDealsActivity.class);
Bundle extras = new Bundle();
extras.putString(Constants.SECTION_NAME, context.getString(R.string.local_deals_section_title));
// Add the offer id to the extras. This will be used to retrieve the coupon details
// in the next activity
extras.putInt(Constants.COUPONS_OFFER_ID, othersDataArray.get(
getAdapterPosition()).getLocalDealId());
couponDetailsItem.putExtras(extras);
context.startActivity(couponDetailsItem);
}
}
#Override
public OthersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.others_items, parent, false);
return new OthersViewHolder(view);
}
#Override
public void onBindViewHolder(OthersViewHolder holder, int position) {
String lfImage = othersDataArray.get(position).getLocalDealImage();
String lfCategoryName = othersDataArray.get(position).getLocalDealSecondTitle();
if (lfCategoryName != null) {
// Set the second title
holder.othersSmallTitleTextView.setText(lfCategoryName);
}
if (lfImage != null) {
if (!lfImage.isEmpty()) {
// Get the Uri
Uri lfUriImage = Uri.parse(lfImage);
// Load the Image
Picasso.with(context).load(lfUriImage).into(holder.othersImageView);
}
}
}
#Override
public int getItemCount() {
return othersDataArray.size();
}
}
I like to point out couple of things -
I've checked other answers on Stack Overflow. They talk about setting the recycler view layout_height to wrap_content. This isn't the issue as the layout_height is already wrap_content and also the second tab loads all the data as expected.
And some others answers mentioned to used same versions for all support libraries and I'm already using 25.1.0 version for all the support libraries.
Size of the data array is 20 and returning 20 from the adapter's getItemCount() method.
The data array has the expected number of items in it and they are not null or empty.
Clean build, invalidate/caches doesn't work either.
Finally, I'm using FragmentStatePagerAdapter to load the fragments when the tabs are in focus.
EDIT:
This is how I'm parsing the JSON data received
private void parseLocalDeals(String stringResponse) throws JSONException {
JSONArray localJSONArray = new JSONArray(stringResponse);
// If the array length is less than 10 then display to the end of the JSON data or else
// display 10 items.
int localArrayLength = localJSONArray.length() <= 20 ? localJSONArray.length() : 20;
for (int i = 0; i < localArrayLength; i++) {
// Initialize Temporary variables
int localProductId = 0;
String localSecondTitle = null;
String localImageUrlString = null;
JSONObject localJSONObject = localJSONArray.getJSONObject(i);
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_ID)) {
localProductId = localJSONObject.getInt(JSONKeys.KEY_LOCAL_DEAL_ID);
}
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_CATEGORY)) {
localSecondTitle = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_CATEGORY);
}
if (localJSONObject.has(JSONKeys.KEY_LOCAL_DEAL_IMAGE)) {
localImageUrlString = localJSONObject.getString(JSONKeys.KEY_LOCAL_DEAL_IMAGE);
}
if (localImageUrlString != null) {
if (!localImageUrlString.isEmpty()) {
// Remove the dots at the start of the Product Image String
while (localImageUrlString.charAt(0) == '.') {
localImageUrlString = localImageUrlString.replaceFirst(".", "");
}
// Replace the spaces in the url with %20 (useful if there is any)
localImageUrlString = localImageUrlString.replaceAll(" ", "%20");
}
}
LocalDealsDataFields localDealsData = new LocalDealsDataFields();
localDealsData.setLocalDealId(localProductId);
localDealsData.setLocalDealSecondTitle(localSecondTitle);
localDealsData.setLocalDealImage(localImageUrlString);
localDealsDataArray.add(localDealsData);
}
// Initialize the Local Deals List only once and notify the adapter that data set has changed
// from second time. If you initializeRV the localDealsRVAdapter at an early instance and only
// use the notifyDataSetChanged method here then the adapter doesn't update the data. This is
// because the adapter won't update items if the number of previously populated items is zero.
if (localDealsCount == 0) {
if (localArrayLength != 0) {
// Populate the Local Deals list
// Specify an adapter
localDealsRVAdapter = new OthersAdapter(context, localDealsDataArray);
localDealsRecyclerView.setAdapter(localDealsRVAdapter);
} else {
// localArrayLength is 0; which means there are no rv elements to show.
// So, remove the layout
contentMain.setVisibility(View.GONE);
// Show no results layout
showNoResultsIfNoData(localArrayLength);
}
} else {
// Notify the adapter that data set has changed
localDealsRVAdapter.notifyDataSetChanged();
}
// Increase the count since parsing the first set of results are returned
localDealsCount = localDealsCount + 20;
// Remove the progress bar and show the content
prcVisibility.success();
}
parseLocalDeals method is inside a helper class and it is called by using initializeHotels.initializeRV();
initializeRV() initializes the Recycler view, makes a network call to the server and the received data is passed to the parseLocalDeals method. initializeHotels being an instance variable of the Helper class.
EDIT 2:
For those who wants to explore the code in detail, I've moved the part of the code to another project and shared it on Github. Here is the link https://github.com/gSrikar/TabLayout and to understand the hierarchy check out the README file.
Can anyone tell me what I'm missing?
Not much of an answer but too long for a comment.
I have duplicated (almost) your adapter code and it fully works for me. I believe I have done the same as you. I'm using the same layout file, the same item & same adapter for all tabs. I think there are no problems with your adapter code.
I say 'almost' because I had to change a couple of things since I don't have access to your data. I changed your LocalDealsDataField model to include a BitmapDrawable & I changed onBindViewHolder() to handle it.
BitmapDrawable lfImage = othersDataArray.get(position).getLocalDealImage();
holder.othersImageView.setBackground(lfImage);
Since there seems to be no problem with your adapter, I would focus on getting the data or setting up the adapter as your problem. Sorry I can't be of help beyond that.
FYI, here's how I setup the adapter in onCreateView()
rootView = inflater.inflate(R.layout.recycler_view, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mAdapter = new OthersAdapter(this.getContext(), list);
mRecyclerView.setAdapter(mAdapter);
Summary
Solved the layout issue at point 1 replacing a LinearLayout by a RelativeLayout, inverting visibility logic to avoid ghost effect and catching exceptions and preventing them when the related view is not found.
Added point 2 to demonstrate that the visual defect is only present on Marshmallow and Nougat devices.
Finally FragmentStatePagerAdapter loads pages before getting focus so a fix is proposed at point 3 (load all pages and update them when are selected).
Further information in the comments below and #d4h answer.
The fourth page is not using the same layout, only the same RecyclerView and id, perhaps a work in progress. The layout issue can be solved using the same layout that previous pages but I consider this change out of scope.
1. Partially fixed for Marshmallow and Nougat devices. Work in progress.
Update2 Changing LinearLayout by RelativeLayout and inverting visibility logic solves layout issue:
Update: Commenting initializeTrending in all the fragment initializations also works onApi23+
I'll check it later, seems as deals are correctly loaded but then trending is loaded and deals are lost. WIP here.
If trending array empty and trending view gone, deals are not shown, but using invisible are shown
2. You are loading a wrong page on Marshmallow and Nougat devices
FragmentStatePagerAdapter first call to getItem() wrong on Nougat devices
This ended up having nothing to do with the FragmentStatePagerAdapter
code. Rather, in my fragment, I grabbed a stored object from an array
using the string ("id") that I passed to the fragment in init. If I
grabbed that stored object by passing in the position of the object in
the array, there was no problem. Only occurs in devices with Android 7.
FragmentStatePagerAdapter - getItem
A FragmentStatePager adapter will load the current page, and one page
either side. That is why it logs 0 and 1 at the same time. When you
switch to page 2, it will load page 3 and keep page 1 in memory. Then
when you get to page 4 it will not load anything, as 4 was loaded when
you scrolled to 3 and there is nothing beyond that. So the int that
you're being given in getItem() is NOT the page that is currently
being viewed, is the one being loaded into memory. Hope that clears
things up for you
These comments are confirmed in this branch and commit
All pages load correctly on Lollipop emulator, the last page has an extra issue, see OthersFragment:
3. Initialize all pages at creation and update them on selection.
Increase OffScreenPageLimit so all pages are initialised
Add on page selected/unselected/reselected listener
These changes solve the issue commented below:
/**
* Implement the tab layout and view pager
*/
private void useSlidingTabViewPager() {
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
BottomSectionsPagerAdapter mBottomSectionsPagerAdapter = new BottomSectionsPagerAdapter(getChildFragmentManager());
// Set up the ViewPager with the sections adapter.
ViewPager mBottomViewPager = (ViewPager) rootView.findViewById(R.id.local_bottom_pager);
mBottomViewPager.setOffscreenPageLimit(mBottomSectionsPagerAdapter.getCount());
mBottomViewPager.setAdapter(mBottomSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(mBottomViewPager);
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
/**
* Called when a tab enters the selected state.
*
* #param tab The tab that was selected
*/
#Override
public void onTabSelected(TabLayout.Tab tab) {
// TODO: update the selected page here
Log.i(LOG_TAG, "page " + tab.getPosition() + " selected.");
}
/**
* Called when a tab exits the selected state.
*
* #param tab The tab that was unselected
*/
#Override
public void onTabUnselected(TabLayout.Tab tab) {
// Do nothing
Log.i(LOG_TAG, "Page " + tab.getPosition() + " unselected and ");
}
/**
* Called when a tab that is already selected is chosen again by the user. Some applications
* may use this action to return to the top level of a category.
*
* #param tab The tab that was reselected.
*/
#Override
public void onTabReselected(TabLayout.Tab tab) {
// Do nothing
Log.i(LOG_TAG, "Page " + tab.getPosition() + " reselected.");
}
});
}
Previous Comments:
Check your LocalFragment getItem() method using breakpoints.
If you select one page, next page is also initialized, and you are sharing the recyclerView, etc.
I would move the initialization outside of getItem() as suggested here:
ViewPager is default to load the next page(Fragment) which you can't
change by setOffscreenPageLimit(0). But you can do something to hack.
You can implement onPageSelected function in Activity containing the
ViewPager. In the next Fragment(which you don't want to load), you
write a function let's say showViewContent() where you put in all
resource consuming init code and do nothing before onResume() method.
Then call showViewContent() function inside onPageSelected. Hope this
will help
Read these related questions (the first has possible workarounds to hack the limit to zero):
ViewPager.setOffscreenPageLimit(0) doesn't work as expected
Does ViewPager require a minimum of 1 offscreen pages?
Yes. If I am
reading the source code correctly, you should be getting a warning
about this in LogCat, something like:
Requested offscreen page limit 0 too small; defaulting to 1
viewPager.setOffscreenPageLimit(couponsPagerAdapter.getCount());
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
I have looked at your code, problem is same as explained by #ardock
Solution i would like to propose,
You have to change your code at 3 place ::
Inside all Fragment You are using in ViewPager Don't call initializeRESPECTIVEView() from onCreateView method.
Inside LocalFragment make a list of Fragments you are going to use with ViewPager and pass it to BottomSectionsPagerAdapter. and return Fragment from that list from getItem(int position) of BottomSectionsPagerAdapter.
Add Following code to LocalFragment inside useSlidingTabViewPager().
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
//Call Respective fragment initializeRESPECTIVEView() method from onTabSelected , you can get fragment instance from list you passed to BottomSectionsPagerAdapter

BottomSheetDialog/BottomSheetDialogFragment — which to use and how?

I'm working on a Material design app. One feature I want to implement is some kind of a poll. When a user clicks an element of a list, the persistent bottom sheet dialog, which looks like this should show up:
Then, when user clicks any button, this dialog should go away and the modal bottom sheet dialog should show up, providing a user with more information about the list item which was clicked at the beginning. It looks like this:
I can't find any clear explanations about BottomSheetDialog and BottomSheetDialogFragment, and how to use them correctly, even after reading some information about AppCompat dialogs. So, my questions are:
In what way are they different and which one should I use for each
case?
How to get data in the activity about which button was pressed in the dialog?
Any links to the code of implementations or tutorials about using them?
Finally, I've found the solution and it works. Tell me if I'm doing something wrong. It basically works like DialogFragment from this guide, but I've done it a bit different.
1) Their difference is the same as it of DialogFragment and Dialog, and they both are modal. If you need persistent dialog, use BottomSheetBehaviour instead (I found out that both dialogs had to be modal in my app).
2) I have to answer the third question with some code first, and then it will be easy to answer the second one.
3) Create a new public class, which extends BottomSheetDialogFragment, I called it FragmentRandomEventPoll. There are two two things which have to be implemented here.
Override method onCreateView. It is nearly the same as onCreate method in Activities, except for that it returns the View it should inflate:
// We will need it later
private static String headerItem;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_random_event_poll, container, false);
header = (TextView) v.findViewById(R.id.uRnd_fragment_bottom_sheet_poll_header);
skip = (Button) v.findViewById(R.id.uRnd_fragment_bottom_sheet_button_skip);
header.setText(...);
// I implemented View.OnClickListener interface in my class
skip.setOnClickListener(this);
return v;
}
Static method which you can pass necessary data to and get new instance of this class (Probably I could have just used a regular constructor, I'll have to experiment with it a bit more). URandomEventListItem is the data model class.
public static FragmentRandomEventPoll newInstance(URandomEventListItem item) {
FragmentRandomEventPoll fragment = new FragmentRandomEventPoll();
headerItem = item.getHeader();
return fragment;
}
2) To get input events in activity or any other place, define an interface with necessary methods and create setter method for it's instance:
private PollButtonClickListener listener;
public void setListener(PollButtonClickListener listener) {
this.listener = listener;
}
public interface PollButtonClickListener {
void onAnyButtonClick(Object data)
}
And in the place you want to get your data ("dialog_event_poll" tag was specified in the layout):
FragmentRandomEventPoll poll = FragmentRandomEventPoll.newInstance(events.get(id));
poll.setListener(new FragmentRandomEventPoll.PollButtonClickListener() {
#Override
public void onAnyButtonClick(Object data) {
// Do what you want with your data
}
});
poll.show(getSupportFragmentManager(), "dialog_event_poll");
}
If there is anything unclear, my project files could be found on Github.
About handling events from DialogFragment/BottomSheetDialogFragment.
For applications with many activities, this method is great:
context as MyDialogFragmentListener
But I have a problem with an application with single activity and multiple fragments. Since there can be a lot of fragments, it seems like a very bad option to transfer all events to the necessary fragments through the main activity. Therefore, I decided to do this:
private inline fun <reified T> findListeners(): ArrayList<T> {
val listeners = ArrayList<T>()
context?.let {
if (it is T) listeners.add(it)
if (it is AppCompatActivity) {
it.supportFragmentManager.fragments.forEach { fragment ->
if (fragment is T) listeners.add(fragment)
fragment.childFragmentManager.fragments.forEach { childFragment ->
if (childFragment is T) listeners.add(childFragment)
}
}
}
}
return listeners
}
Code in DialogFragment:
private val listeners by lazy { findListeners<MyDialogFragmentListener>() }
Of course, fragments can contain as many other fragments as you like and probably need to be checked through recursion, but in my case this is superfluous.

Listview smooth scroll until item is at top

I am working on an android application, I have a list view with 10 items. I need to do the following. When the user clicks a button, I want the list to smooth scroll to item at position 5, so this item is displayed on the top of the list.
I have found 2 methods that can be used for this, but both methods are not working exactly how I need:
listView.setSelection(5) this will scroll to the row and put it on top of the list But without animation
list.smoothScrollToPosition(5) this will scroll the listview untill the row is visible but it will not put it on top (it is at the bottom of the page) and if the row is allready visible it will not scroll as it considers it is visible.
So is there a way to have the same behavior as the setSelection method but with smoothscrolling?
Thank you
I believe smoothScrollToPositionFromTop() does what you want.
There's also one that will take the desired animation duration in milliseconds as an argument.
So is there a way to have the same behavior as the setSelection method
but with smoothscrolling?
You could post a delayed Runnable and create your own smooth scroll effect using ListView.setSelection. Here's an example:
private ListView mListView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mListView.post(new PositionScroller(this));
}
private static final class PositionScroller implements Runnable {
private static final int SMOOTH_SCROLL_DURATION = 25;
private int mSelectedPosition;
private final WeakReference<YourParentActivity> mParent;
private PositionScroller(YourParentActivity parent) {
mParent = new WeakReference<YourParentActivity>(parent);
}
#Override
public void run() {
final ListView list = mParent.get().mListView;
if (mSelectedPosition <= 5) {
if (list.postDelayed(this, SMOOTH_SCROLL_DURATION)) {
list.setSelection(mSelectedPosition++);
}
}
}
}

How do I call a fragment from inside a custom view

DogActivity is using a custom View. The custom view handles some logic and so has fields. When a particular field reaches a certain value, I want to start a fragment whose parent is DogActivity. How would I do that?
Is it advisable to put a callback inside a custom view so that it calls its parent activity? Or is there a simpler way?
When programming you should always look for consistency, i.e. look around you and see how similar stuff to what you want to do is done. The Android SDK makes heavy use of callback listeners, so they are the way to go here.
In fact we don't even need to know what kind of View your CustomView really is, we can build a general purpose solution. Don't forget to adapt/optimize according to your specific surroundings however. And think about abstraction and generalisation once you get to a point where all your Views are spammed with listeners!
You will need 3 things:
A listener interface
public interface OnCountReachedListener {
public void onCountReached();
}
A place to accept the listener and a place to alert the listener in your CustomView
public class CustomView extends View {
private int theCount;
private OnCountReachedListener mListener;
public void setOnCountReachedListener(OnCountReachedListener listener) {
mListener = listener;
}
private void doSomething() {
while (theCount < 100) {
theCount++;
}
// The count is where we want it, alert the listener!
if (mListener != null) {
mListener.onCountReached();
}
}
An implementation of the interface in your Activity
public class DogActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
View myView = new CustomView();
myView.setOnCountReachedListener(new OnCountReachedListener() {
#Override
public void onCountReached() {
Log.w("tag", "COUNT REACHED!");
// START YOUR FRAGMENT TRANSACTION HERE
}
});
}
}
For further information look at the source code of the View class and all the On**XY**Listener interfaces in the Android SDK. They should give you plenty to think about
What is the type of the field? Is it an EditText? SeekBar? Depending on the View, you'll be able to specify different listeners/callbacks to determine when they have changed and if they've reached a certain threshold. I would attach these listeners within onCreate of DogActivity. When the threshold is reached, use a FragmentTransaction to add your Fragment as the child of a container View in DogActivity.

Swipe / (Fling) with dynamic views

What i want to:
I want to add a swipe or what i learned it's named on android fling, to my app.
I have a dynamic number of views, the number is the amount of dates from an ICS file which i parse, i want to make a swipe effekt on all of these views together.
But if i have let's say 12 of these each having a ListView with 8 items (max) it would use a lot of memory i guess. So i would like that only the 2 before current selected view and the 2 after to be initialized.
What i have tried:
In my search i stumpled around this stackoverflow question which mentions HorizontalPager. But i dont know to make it work with a number of ListView's and load them dynamically.
I tried a ViewGroup and then add and remove a ListView but it's not working, it display's the ViewGroup but not the ListView
public class HourView extends ViewGroup
{
private ListView listView;
/**
* #param context
*/
public HourView(Context context)
{
super(context);
init(false);
}
/**
*
* #param context
* #param day the current day
*/
public HourView(Context context, String day,
boolean shouldBeVisible)
{
super(context);
this.day = day;
init(shouldBeVisible);
}
private void init(boolean shouldBeVisible)
{
if (shouldBeVisible)
{
listView = new ListView(getContext());
if (day == null)
{
day = Event.dtFormatDay.format(new Date());
}
new GetEvents(day).execute();
addView(listView);
}
else
{
removeAllViews();
listView = null;
}
}
}
The GetEvents() is a AsyncTask (a class inside the viewgroup class) that gets some events from a local database, the code is the onPostExecute is as follows
protected void onPostExecute(String errMsg)
{
if (errMsg != null)
{
listView.setAdapter(new SkemaHoursRowAdapter(getContext(), eventItems));
listView.requestLayout();
addView(listView, layoutParams);
}
}
eventItems is an array i parse to my custom rowadapter (which i know works). The view group is displayed but the listView is not.
Any suggestions??
I ended up not using a viewgroup instead i made a loader-listadapter which i display if the correct list has not been updated yet.
So instead of extending ViewGroup it extends ListView and it's adapter get set at at first to the load-adapter and when loaded it sets it to the correct listview.

Categories

Resources